Android线程通信模型(Handler and Loop)
今天在看IntentService的时候,发现自己对于线程通信模型并不十分了解。于是把Handler and Loop模型重新学了一遍。总结一下加深记忆。
##模型分析
首先我们来考虑一下线程之间需要哪些通信,我们需要实现哪些功能。
- 回调功能(处理信息的能力)
- 在某某事件结束后,执行一个方法。
- 延迟一段时间后,执行一个方法。
- 发现信息的能力
- 把信息送往正确目的地的能力
##主要用法和实现
###用法
主要用法有两种,一种为view.postXXX()
系;另一种为直接调用Handler
产生。
调用handler时1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class myActivity extends Activity{
Handler mHandler;
proteced void onCreate(){
mHandler = new Hanlder() {
public void handleMessage(Message msg) {
//onHandleIntent((Intent)msg.obj);
//stopSelf(msg.arg1);
}
}
}
new Thread(new Runable(){
public void run(){
Message msg = mHandler.obtainMessage();//这里是Message池
mHandler.sendMessage(msg);//内部调用,sendMessageDelayed(msg,0);
}
})
}
###原理
在某一线程中调用mHandler.sendMessage(Message msg)
方法,并且由同一个mHanlder
引用在另一个特定线程中处理
那么问题来了:
要传递的信息从哪来?
是什么?
准备到哪里去?
####从哪来?
mHandler.obtainMessage(){ return Message.obtainMessage(this)}
- 调用Message类的静态方法
Message.obtainMessage(Handler handler)
此处的handler将被传给msg.target变量决定到哪里去问题
- Message的静态方法有统一调用
obtain()
方法,从global message pool
中获取一个Message实例globle message pool
的实现值得一提- 这个pool使用链表来实现,靠
msg.next()
链接起来。在类中只保存一个引用。1
2
3
4
5
6
7
8
9
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/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
```
###是什么?
Handler只是传递一些简单的信息
- `int what` 通常是用来在`handleMessage`时做判断信息是由哪个地方来的
- `int arg1` 额外变量
- `int arg2` 额外变量
- `Object object` 可以传递一些Runable方法
###到哪去?
`sendMessage`会把该信息打上`target`标签,然后入队(这里传入`MessageQueue`,还不是`Loop`)`Handler` 中的`enqueueMessage`会调用`MessageQueue`的`enqueueMessage`
```java
//class Handler
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}// lock to make it asynchronous
return queue.enqueueMessge(msg, uptimeMillis);
}
此处所入的队列Queue
由msg.next
的连接起来的链表,队列的顺序受时间的影响,即时间靠前的排在前面。特殊情况:如果比表头还靠前,需要更新表头,此时要多加一句needWake = mBlocked;
,以便后面nativeWake(mPtr)
唤醒。
注意: 表头保存在MessageQueue
类mPtr
中
Handler
中的这个MessageQueue
实例由Looper
实例获得,Looper
实例由Looper.myLooper()
静态方法获得(或者初始化时传入的Looper
),这一动作在构造函数中进行。
所以,信息到底被传递给哪个线程,关键在于Handler
在哪里初始化,或者初始化时传入的Looper
参数
想获取特定的Looper
对象,只要用thread.getLooper
就好
Looper
和MessageQueue
是绑定的,一对一的聚合关系。一个线程内,Looper
的初始化函数会调用MessageQueue
的初始化函数。
###取与送
如何把特定的Message
交给特定的Handler
?我们需要把Message
从MessageQueue
中取出来,并且送到特定的Handler
中,这个工作由Looper
负责。
Looper与线程是一一对应的,也就是说,每条线程有且只能有一个Looper来管理信息。
这种一一对应的关系是由Looper类中的一个静态变量mThreadLocal
实现的。
该变量的定义:1
2// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
再看Looper.prepare()
方法:1
2
3
4
5
6private 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));
}
以及Looper.myLooper()
方法:1
2
3
4
5
6
7/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
ThreadLocal是一种特殊的多线程变量结构,在Python等语言中也有不同程度的支持1
2
3
4
5
6
7
8
9
10
11
12
13/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code ThreadLocal} object,
* but each sees a different value when accessing it, and changes made by one
* thread do not affect the other threads. The implementation supports
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
*/
public class ThreadLocal<T> {
...
}
该类由一个哈希表来实现,由每个线程本身作为key,取出value
Looper类用死循环来取信息,该动作在Looper.loop()
方法实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block(这里面也有一个死循环)
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
注意: Looper
(以及其包含的MessageQueue
)都与线程关联,后者与前者同时初始化。二者中皆包含死循环,来不断的读取信息。
再注:在主线程中肯定不能有死循环,该怎么办呢?Looper
中专门为mainThread
留了一个变量sMainLooper
,其实还是由myLoop()
方法赋值,也就是说还是保存在mThreadLocal
中。
(傻逼了)
主线程这里也是这个死循环,不停的更新UI,读缓存绘制到屏幕上。其它GUI正这种死循环也非常常见,典型的控制反转
##应用范例:IntentService
IntentService 可以在另一个线程中异步的执行一个任务。那么它是怎么在不同的线程中通讯的呢?
1 | public abstract class IntentService extends Service{ |
其实说白了,就是Handler在主线程中把信息发给子线程,并且在子线程中执行Handler的handleMessage方法。
这是因为,Handler的初始化时传入了特定的Looper,也就与子线程绑定了。Looper的死循环在子线程中进行,自然dispatchMessage()以及handleMessaged()方法都在子线程中进行。也就实现了异步任务。