上一篇主要说了:
(1)Message,MessageQueue,Looper,Handler的工作原理就像是工厂的生产线。待加工的产品就是Message,“传送带”就是MessageQueue,电动机就是Looper,工人们就对应于处理事件的Handler。
(2)Android系统用链表来实现Message的缓存消息池。
我们知道Android的应用程序的入口是ActivityThread.main()方法。在该方法中首先会创建Application和默认启动的Activity,并且将他们关联在一起。而该应用的UI线程的消息循环也是在这个方法中创建的,具体代码如下:
public static void main(String[] args) {
//代码省略
Process.setArgV0("<pre-initialized>");
//1,创建消息循环Looper,就是UI线程的Looper
Looper.prepareMainLooper();
// 2,启动ActivityThread,这里最终会启动应用程序
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 3,执行消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
执行了ActivityThread.main方法后,应用程序就启动了,UI线程的消息循环也在Looper.loop函数中启动了。此后Looper就一直从消息队列中取消息,然后Handler处理消息。用户或者系统通过调用Handler不断地往消息队列中添加消息,这些消息不断地被取出、处理、回收,使得应用程序迅速地运转起来。
例如:我们在子线程中执行完耗时操作后通常需要更新UI,但我们都知道不能再子线程中更新UI。此时最常用的手段就是通过Handler将一个消息post到UI线程中,然后再在Handler的handleMessage方法中进行处理。
但是有一点要注意,就是如果用在不传递UI线程的Looper的情况下,那么该Handler必须在主线程中创建。
为什么必须这么做呢?
其实每个Handler都会关联一个消息队列,消息队列被封装在Looper中,而每个Looper又是ThreadLocal的,也就是说每个消息队列只会属于一个进程。因此如果一个Looper在进程A中创建,那么该Looper只能被该线程访问。而Handler则是一个消息投递,处理器,它将消息投递给消息队列,然后这些消息在消息队列中被取出,并且执行在关联在消息队列的线程中。默认情况下,UI线程也就是调用了Looper.getMainLooper方法,创建Looper之后最后会执行Looper.loop方法开启消息循环。
那么Handler是如何关联消息队列和线程的呢??
我们先看一下Hnadler的构造函数:
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public Handler(Callback callback, boolean async) {
//代码省略
mLooper = Looper.myLooper();//获取Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//获取消息队列
mCallback = callback;
mAsynchronous = async;
}
我们看到myLooper方法是通过sThreadLocal.get()来获得的。那么Looper对象又是在什么时候存进去的呢?在上面的源代码中我们发现prepare方法中创建了一个Looper对象并且将该对象设置给了sThreadLocal,这样队列就与线程关联上了。
我们再回到Handler中来,Looper属于某个线程,消息队列存储在Looper中,因此,消息队列就通过Looper与特定的线程关联上。而Handler又与Looper、消息队列关联,因此,Handler最终就和线程、线程的消息队列关联上了,通过该Handler发送的消息最终就会被执行在这个线程上。这就能解释上面提到的问题了,“在不传递Looper参数给Handler构造函数的情况下,用更新UI的Handler“必须在主线程中创建?”就是因为Handler要与主线程的消息队列关联起来,这样handlerMessage才会执行在UI线程中,更新UI才是被允许的。
创建了Looper之后,会调用Looepr的loop函数不断地从消息队列中取出消息。具体代码如下:
public static void loop() {
final Looper me = myLooper();//获取Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//通过Looper对象拿到消息队列
final MessageQueue queue = me.mQueue;
// 代码省略
for (;;) {//死循环,即消息循环
Message msg = queue.next();
if (msg == null) {
return;
}
//消息分发处理
msg.target.dispatchMessage(msg);
//代码省略
//回收消息
msg.recycleUnchecked();
}
}
从上述代码可以看出,loop方法其实就是建立一个死循环,容纳后通过从消息队列中逐个取出消息,最后就是处理消息,回收消息的过程。代码中调用的是MessageQueue的next函数获取下一条要处理的消息。这个MessageQueue在Looper的构造函数中构建。