前言
前篇对MessageQueue、Handler等几个概念进行了概述,相信大家一定也有了一定的理解。接下来将对上篇遗留的问题进行研究。由于上面中LooperThread例子只是一个壳,没有可真正运行的”内容“。所以要回答剩余的问题,ActivityThread是一个很好的示例。从名称上看ActivityThread就是我们所熟悉的主线程。
示例
public static void main(String[] args) {
...
Looper.prepareMainLooper() ;
ActivityThread thread = new ActivityThread();
thread.attach(false);
if ( sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
Looper,loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
如果比较上面这段代码与LooperThread.run()的实现,就可以发现它们在整体架构上是一样的,区别主要体现在:
- prepareMainLooper和prepare
普通线程只要prepare就可以了;而主线程使用的是prepareMainLooper。 - Handler 不同
普通线程生成一个与Looper绑定的Handler对象的就行;而主线程是从当前当前线程中获得的Handler(thread.getHandler());
- 那么,prepareMainLooper有什么特殊之处?
public static void prepareMainLooper() {
prepare (false); //先调用prepare
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper ();
}
}
我们可以看到,在prepareMainLooper也是需要用到prepare.参数false表示该线程不允许退出,这和前面的LooperThread不一样,经过prepare后,myLooper就可以得到一个本地线程<ThreadLocal>的Looper对象,然后将其赋给sMainLooper。从这个角度来讲,主线程的sMainLooper其实和其他线程的Looper对象并没有本质的区别。
Looper揭秘.png
这个图描述的是一个进程和它内部两个线程的Looper情况,其中线程1是主线程,线程2是普通线程。方框表示它们能访问的范围,如线程1就不能直接访问到线程2中的Looper对象,但二者都可以接触到进程中的各元素。
线程1:因为是Main Thread,它使用的是prepareMainLooper(),这个函数将通过prepare()为线程1生成一个ThreadLocal的Looper对象,并让sMainLooper指向它。这样做的目的就是其他线程如果要获得主线程的Looper,只需调用getMainLooper()即可。
线程2:作为普通线程,它调用的是prepare();同时也生成一个ThreadLocal的Looper对象,只不过这个对象只能在线程内通过myLooper()访问。当然,主线程内部也可以通过这个函数访问它的Looper对象。
由此可见,Google玩了一个技巧,从而巧妙的区分开各线程的Looper,并界定了它们的访问权限。
- sMainThreadHandler。当ActivityThread对象创建时,会在内部同时生成一个继承自Handler的H对象:
final H mH = new H();
ActivityThread.main中调用的tread.getHandler()返回的就是mH。
也就是说,ActivityThread提供了一个“事件管家”,以处理主线程中的各种消息。
接下来我们分析下loop()函数。
public static void loop() {
final Looper me = myLooper () ;
/*loop函数也是静态的,所以它只能访问静态的数据。函数myLooper则调用sThreadLocal.get()来获取与之匹配的Looper实例(其实就是取出之前prepare中创建那个Looper对象)*/
...
final MessageQueue queue = me.mQueue;
/*正如我们所说,Looper中自带一个MessageQueue*/
for( ; ; ){//消息循环开始
Message msg = queue.next();/*从MessageQueue中取出一个消息,可能会阻塞*/
if(msg == null){
/*如果当前消息队列中没有msg,说明线程要退出了。类比于上面Windows伪代码
例子中while判断条件为0,这样就会结束循环*/
return;/*消息处理完毕,进行回收*/
}
...
msg.target.dispatchMessage(msg);
/*终于开始分派消息,重心就在这里。变量target其实是一个Handler,所以
dispatchMessage最终调用的是Handler中的处理函数*/
...
msg.recycle () ;/*消息处理完毕,进行回收*/
}
}
可以看到,loop()函数的主要工作就是不断地从消息队列中取出需要处理的事件,然后分发给相应的负责人。如果消息队列为空,它很可能会进入睡眠以让出cpu资源。而在具体事件的处理过程中,程序会post新的事件到队列中。另外,其他进程也可能投递新的事件到这个队列中。APK应用程序就会不停地执行“处理队列事件”的工作,直到它退出运行。
以上我们看到了Looper.loop的处理流程,从而知道它和前面讨论的Windows消息处理机制是类似的,最后再来解决一个问题:MessageQueue是怎样创建出来的?
我们有提到过,Looper中带有唯一一个MessageQueue,是不是这样?
/*
以下代码还是Looper.java中的,不过只提取出MessageQueue相关的部分
*/
final MessageQueue mQueue ; /*注意它不是static的*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue( quitAllowed );
/*new了一个MessageQueue,就是它了。也就是说,当Looper创建时,消息队列也同时会被创建出来*/
mRun = true;
mThread = Thread.currentThread();//Looper与当前线程建立对应关系
}
事实证明Looper内部的确管理了一个MessageQueue,它将作为线程的消息存储仓库,配合Handler,Looper一起完成一系列操作。
网友评论