android中处理一些费时的操作需要单独启动一个子线程去处理。子线程处理完毕将结果通知给UI主线程更新UI界面。 子线程与UI主线程的通信在android中使用了消息机制来完成,那么是怎么完成的呢?这就和handler机制的原理(android中线程间通信的原理,而不是进程间通信), 简而言之,就是需要两样古老的东西,消息队列、轮询。也就是说,主线程起来以后有一个消息队列,同时和该队列配对的有一个轮询, 而子线程有这个消息队列的引用,那这样,子线程处理完以后就会向主线程的消息队列发消息,主线程轮询自己的队列,发现有未处理的消息就进行处理。 这就是handler的机制。
looper对象拥有message queue,并且负责从message queue中取出消息给handler来处理。同时handler又负责发送message给looper,由looper把message添加到message queue尾部。 handler和looper是来联系在一起的。多个message可以指向同一个handler,多个handler也可以指向同一个looper。
Handler
消息的处理者,handler负责将需要传递的信息封装成Message,通过调用handler对象的obtainMessage()来实现。将消息传递给Looper, 这是通过handler对象的sendMessage()来实现的。继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message, 就将其分发出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。
Handler一些特点:handler可以分发Message对象和Runnable对象到主线程中,每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 它有两个作用: (1)安排消息或Runnable在某个主线程中某个地方执行。(2)安排一个动作在不同的线程中执行。
Message
消息对象,Message Queue中的存放的对象。一个Message Queue中包含多个Message。Message实例对象的取得, 通常使用Message类里的静态方法obtain(),该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例, 而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例, 则才用给定的参数创建一个Message对象。调用removeMessages()时,将Message从Message Queue中删除,同时放入到Message Pool中。 除了上面这种方式,也可以通过Handler对象的obtainMessage()获取一个Message实例。
MessageQueue
消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。 通常使用一个Looper对象对该线程的MessageQueue进行管理。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建, 将自动创建一个Message Queue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用Looper的prepare函数来实现。
Looper
Looper是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过prepare函数来实现的。 同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象 创建一个Looper对象时, 会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有MessageQueue对象的,所以,不能接受Message。 如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了,而且一个线程只能有一个looper。 调用完prepare以后,此线程就成为了所谓的LooperThread,若在当前LooperThread中创建Handler对象,那么此Handler会自动关联到当前线程的looper对象,也就是拥有looper的引用。 Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message Pool中。
源码解析
handler其构造函数:
[代码]java代码:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
|
构造方法中无参构造和没有传递looper的构造最终调用了public Handler(Callback callback, boolean async); 在这个方法中获得当前线程的looper,再使用这个looper获得looper上的message queue。
而如果构造中传递了looper,则调用 public Handler(Looper looper, Callback callback, boolean async), 用传递进来的looper代替默认的looper,然后再同样的初始化mQueue,mCallback,mAsynchronous。
先看mLooper = Looper.myLooper();这一句发生了什么:
[代码]java代码:
1 2 3 |
|
可以看到,该方法返回一个sThreadLocal对象中保存的Looper。如果尚未在当前线程上运行过Looper.prepare()的话,myLooper会返回null。 接下来看看Looper.prepare()的实现:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 |
|
可以看到该方法只是简单地新建了一个Looper对象,并将其保存在sThreadLocal中。在注释中说明了prepare()要在loop()之前调用,最后 调用quit()。接下来看一下Looper的构造函数。
[代码]java代码:
1 2 3 4 |
|
调用完Looper.prepare()后,需调用Looper.loop()才能使消息循环运作起来,其源码如下所示:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
在loop()主要干了四件事:
1.调用Looper me = myLooper()取出looper对象。
2.调用MessageQueue queue = me.mQueue; 取出looper绑定的message queue。
3.死循环调用Message msg = queue.next();在message queue中取数据,若msg为null就不执行下面的分发,跳出死循环。 这里为什么敢直接跳出循环?因为上一行的queue.next()里也是一个循环,这个循环实现很巧妙,只有遇到message时才取出,否则阻塞。 一旦queue.next()里的循环阻塞了,外面的for循环也阻塞了,避免了空循环浪费资源。queue.next()里的for循环也不是一直做空循环的, 它会根据message里的when字段(postDelay设置的延迟)来计算时间,到时间了才会开始取,其他时间都是阻塞状态。
4.在上面的循环中若msg不为null,调用msg.target.dispatchMessage(msg); 分发message到指定的target handler。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 |
|
分发之后就执行了mCallback中的回调,这个回调就是我们自己覆写的方法public void handleMessage(Message msg)。
从上面代码也可以看出,轮询器在for (;;){}死循环代码块中不断的执行,通过queue.next();从MessageQueue中取出一个Message, 当msg不为空时,执行msg.target.dispatchMessage(msg);(实际上最终是调用到了Handler的dispatchMessage方法去拦截消息)
在Message中的源码中,target就是当前线程的Handler对象,msg的成员变量target是在发送消息的时候设置好的,一般就通过哪个Handler 来发送消息,就通过哪个Handler来处理消息。
接下来看一下Message类:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 |
|
Message中有一个静态类型的Message对象,叫做sPool,同时还有一个叫做next的Message对象,这个应该是指向队列中下一个message的引用。 当我们调用Message.obtain()时,返回了一个Message对象。Message对象使用完毕后,调用recycle()方法将其回收。其中obtain方法的代码如下所示:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 |
|
可以看到,obtain方法被调用时,首先检测sPool对象是否为空,若不为空将其当做新的message对象返回,并指向message对象的next属性, sPoolSize自减。可以看出message对象通过next属性串成了一个链表,sPool为“头指针”。再来看看recycle方法的实现。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
如果message对象不是处于正在被使用的状态,则会被回收。其属性全部恢复到原始状态后,放在了链表的头部。sPool对象“指向”它,sPoolSize自增。 可以看出,通过obtain和recycle方法可以重用message对象。通过操作next、sPoolSync、sPool、sPoolSize这四个属性,实现了一个类似栈的对象池。
下面看看msg对象是如何放到消息队列里面的,通常来说,我们通过Handler的sendMessage(msg)方法来发送消息,其源码如下所示:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
可知sendMessage最终会调用queue.enqueueMessage(msg, uptimeMillis)将msg对象保存至message queue中,uptimeMillis表示msg执行回调的时刻。 我们来看一下MessageQueue类的enqueueMessage方法:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
结合注释,我们可以了解到msg push到queue中时,queue的状态的变化和处理队列的逻辑。 前文中Looper对象的loop方法中:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 |
|
可以看出,message queue的next方法被调用时,可能会发生堵塞。我们来看一看message queue的next方法:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
|
代码执行流程见注释。其中IdleHandler是一个接口:
[代码]java代码:
1 2 3 |
|
IdleHandler提供了一个在MessageQueue进入idle时的一个hook point。更多时与barrier机制一起使用,使message queue遇到barrier时产生一个回调。
总结
前面涉及到的几个主要的类Handler、Looper、MessageQueue和Message的关系如下所述:
1.Handler负责将Looper绑定到线程,初始化Looper和提供对外API。 2.Looper负责消息循环和操作MessageQueue对象。 3.MessageQueue实现了一个堵塞队列。 4.Message是一次业务中所有参数的载体。