Handler

不咋想干活了,写篇关于 Handler 的文章。

先看一下我们平时都怎么用的: 开启一个异步线程 workerThread 然后去 work,然后通过 uiThreadHandler 来通知到主线程。

private final Handler uiThreadHandler = new Handler(Looper.getMainLooper());
private Thread workerThread = null;
private void work() {
	workerThread = new Thread(new Runnable() {
		@Override
		public void run() {
			try {
				Thread.sleep(10 * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			uiThreadHandler.post(new Runnable() {
				@Override
				public void run() {
					// run in mainThread
				}
			});
		}
	});
	workerThread.start();}

Handler 主要的作用就是线程间通讯。

主要涉及的到的概念主要是 Handler、Thread、Looper、MessageQueue、Message。Handler 整个的机制就是把这几个东西整合然后来实现线程间通讯的。因为主要想说清楚线程间通讯,所以会省略其他不是什么相关的东西。

屏幕快照 2017-06-15 上午11.36.07.png

下面我们分而讲之。我们先从最简单的讲起。

Message

因为 Message 并不是重点,我们不做过多介绍

public final class Message implements Parcelable {
	...
	// 下一节点的引用
	Message next;
	
	// Message 的池子
	private static Message sPool;
	...
}

Message.java 中主要包含两个变量,由此可知,Message 其实包含的并不仅仅是本节点的数据,还包含下一个节点的引用。也就是说 Message 其实是一个单链表中的节点。

MessageQueue

其中最主要的一个成员变量:

public final class MessageQueue {
	...
	Message mMessages;
	...
}

刚才也说到了单链表,这里其实 mMessages 就是单链表的头节点。而 MessageQueue 的核心也就是这个单链表。 这里需要注意的是,因为 MessageQueue 会有多个线程访问,所以 MessageQueue 中挺多的代码段都会添加 synchronized。

Looper

public final class Looper {
	...
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
	final MessageQueue mQueue;
	final Thread mThread;
   
  // 准备函数
	private static void prepare(boolean quitAllowed) {
		if (sThreadLocal.get() != null) {
			throw new RuntimeException("Only one Looper may be created per thread");
		}
		sThreadLocal.set(new Looper(quitAllowed));
	}

	// 构造函数
	private Looper(boolean quitAllowed) {
		mQueue = new MessageQueue(quitAllowed);
		mThread = Thread.currentThread();
	}
	....
}

可见,一个 Looper 持有一个 MessageQueue 实例,也持有一个 Thread 的引用,当然这个引用是执行当前代码的线程的引用。 而且构造函数是 private,只能在 prepare 时被 new 出来,然后存入到 sThreadLocal 中。为了不引入太多的复杂度,这里先不介绍 ThreadLocal,你可以先认为这个数据结构可以根据当前的线程获取和这个线程对应的数据结构(这里就是 Looper)。 所以我们知道如果线程想轮询的处理 Message,那么必须要先调用 prepare,因为只有 prepare 后,才能构造出一个和这个 Thread 对应的 Looper。

Handler

先看代码再说结论,Handler 的构造函数:

public class Handler {
	...
	final Looper mLooper;
	...
	public Handler(Looper looper, ...) {
		mLooper = looper;
	}
	...
}

通过代码可以看到,Handler 里边相关的变量只有 Looper,也就是说 Handler 其实都是通过 Looper 来完成所有操作的。所以 Handler 其实与 Thread 并没有直接关系。同样可以知道,只要拿到 Looper 的引用,随时都可以实例化多个 Hander,对于一个 Looper(Thread)可以构造多个 Handler。