Android异步消息机制架构

Android异步消息处理架构,其实没那么复杂。简单来说就是looper对象拥有message queue,并且负责从message queue中取出消息给handler来处理。同时handler又负责发送messagelooper,由loopermessage添加到message queue尾部。就一个圈儿。下面给出图解,应该不难吧?

所以很明显handlerlooper是来联系在一起的。需要说明的是,多个message可以指向同一个handler,多个handler也可以指向同一个looper

还有一点很重要,普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare()方法,而且一个线程只能有一个looper。调用完以后,此线程就成为了所谓的LooperThread,若在当前LooperThread中创建Handler对象,那么此Handler会自动关联到当前线程的looper对象,也就是拥有looper的引用。

Looper

简而言之,Looper就是一个管理message queue的类。我们直接来看下源码好了。

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
38
39
40
public class Looper {
......

private static final ThreadLocal sThreadLocal = new ThreadLocal();

final MessageQueue mQueue;//拥有的消息队列

......

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
//创建新的looper对象,并设置到当前线程中
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
·····
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//获取当前线程的looper对象
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}

private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}

......
}

由源码可知,looper拥有MessageQueue的引用,并且需要调用Looper.prepare()方法来为当前线程创建looper对象。Android注释很详细的说明了这个方法。

This gives you a chance to create handlers that then reference
this looper, before actually starting the loop. Be sure to call
loop() after calling this method, and end it by calling
quit()

也就是说只用在调用完Looper.prepare()之后,在当前的线程创建的Handler才能用有当前线程的looper。然后调用loop()来开启循环,处理message.来简单看一下loop()的源码

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
public static void loop() {
final Looper me = myLooper();//获取looper对象
if (me == null) {
//若为空则说明当前线程不是LooperThread,抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; 获取消息队列
.....
for (;;) {
//死循环不断取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

// This must be in a local variable, in case a UI event sets the logger
//打印log,说明开始处理message。msg.target就是Handler对象
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

//重点!!!开始处理message,msg.target就是Handler对象
msg.target.dispatchMessage(msg);

//打印log,处理message结束
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
.....
}
}

很明显,就是一个大的循环,不断从消息队列出取出消息。然后调用一个很关键的方法msg.target.dispatchMessage(msg)开始处理消息。msg.target就是message对应的handler.其实我一直在重复文章开头的概念。looper对象管理MessageQueue,从中取出message分配给对应的handler来处理。

在分析msg.target.dispatchMessage(msg)方法之前,先让大家了解下messagehandler

Message

Message 就是一些需要处理的事件,比如访问网络、下载图片、更新ui界面什么的。Message拥有几个比较重要的属性。

  • public int what 标识符,用来识别message
  • public int arg1,arg2 可以用来传递一些轻量型数据如int之类的
  • public Object obj Message自带的Object类字段,用来传递对象
  • Handler target 指代此message对象对应的Handler

如果携带比价复杂性的数据,建议用Bundle封装,具体方法这里不讲了。

值得注意的地方是,虽然Message的构造方法是公有的,但是不建议使用。最好的方法是使用Message.obtain()或者Handler.obtainMessage() 能更好的利用循环池中的对象。一般不用手动设置target,调用Handler.obtainMessage()方法会自动的设置Messagetarget为当前的Handler

得到Message之后可以调用sendToTarget(),发送消息给HandlerHandler再把消息放到message queue的尾部

Handler

1
2
3
4
5
6
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback; //handleMessage的借口
mAsynchronous = async;
}

上面是Handler构造器,由此可知,它拥有looper对象,以及loopermessage queue

要通过Handler来处理事件,可以重写handleMessage(Message msg),也可以直接通过post(Runnable r)来处理。这两个方法都会在looper循环中被调用。

还记得刚在loop循环中处理信息的msg.target.dispatchMessage(msg)方法。现在我们来看下这个方法的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void dispatchMessage(Message msg) {
//注意!这里先判断message的callback是否为空,否则就直接处理message的回调函数
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//正是在这调用我们平常重写handleMessage
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

没错,正是在这个方法中调用了handleMessage(Message msg)。值得注意的是,再调用handleMessage(Message msg)之前,还判断了message的callback是否为空。这是为什么?

很简单,这就是post(Runnable r)方法也能用来处理事件的原因。直接看它的源码。我就不赘述了。相信已经很清晰了。

1
2
3
4
5
6
7
8
9
10
11
12
public final boolean post(Runnable r)
{
//获取消息并发送给消息队列
return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
//创建消息,且直接设置callback
Message m = Message.obtain();
m.callback = r;
return m;
}

总结

现在一切都弄清楚了。先调用Looper.prepare()使当前线程成为LooperThread,Looper.loop()开启循环之后,然后创建message,再由Handler发送给消息队列,最后再交由Handler处理。下面是官方给出的LooperThread最标准的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();

mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};

Looper.loop();
}

最后的最后,再提一下,平时在说的主线程也就是UIThread,也是一个LooperThread。这也是为什么我们能在子线程中发送消息,然后在主线程中更新ui。因为Handler是在主线程创建的,所以Handler关联的是主线程的looper,最后给一个更新ui的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static Handler handler=new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.message_activity);
new Thread(new Runnable() {
@Override
public void run() {
// tvTest.setText("hello word");
// 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问
// 使用post方式设置message的回调
handler.post(new Runnable() {
@Override
public void run() {
tvTest.setText("hello world");
}
});
}
}).start();
}

声明

转载写明出处,谢谢。

这是我的微博 有任何问题都可以联系我。Three_ZJ