Android消息异步机制(ThreadLocal、MessageQueue、Looper、Handler关系总结)

网上大神们关于Android消息异步机制的文章已经很多了,并且分析也很到位,我在这主要是为了记录个人体会,有错误的地方欢迎大家指正。

一.概述

Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。
大致的运行过程如下图
这里写图片描述
这里以UI线程(即主线程)与其他线程的交互为例。

MessageQueue用于存储消息(Message)队列(内部存储数据结构是链表)

Looper会以无限循环的方式去查找是否有新的消息(Message),有则处理,没有则等待。

ThreadLocal可以在不同线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。

Handler用于post消息(Message)给其他线程,并完成从其他线程到UI主线程的调转

综上可知 除了Handler运行在主线程,其他三个(MessageQueue,Looper,ThreadLocal运行在子主线程)
为了更好的理解运行时的关系。我们对这四者分别介绍,最后再做总结。

#ThreadLocal#

由上图可知,Handler要想将消息投递给子线程,则必须投递到该子线程对应的Looper中。那么对于一个Handler来说,我怎么知道,我投递的消息是否投递到了A线程,而不是B线程呢,换句话说,Handler又是怎么和我想要的Looper建立起联系的呢?答案就是ThreadLocal!也正是ThreadLocal的存在,让队列与线程关联上了!

ThreadLocal在线程中的作用

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定的线程中才可以获取到该数据,其他线程无法获取(换而言之,当某些数据是以线程为作用域并且不同线程具有不同的数据副本时,就可以考虑使用ThreadLocal)。例如:对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以让Handler轻松获取到想要线程的Looper,从而进行其他操作。(之所以说轻松,是因为如果不采用ThreadLocal的方式来获取Looper,系统为了让Handler来得到想要的线程的Looper,则必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个类似LooperManager的类的管理,增加了使用成本)

ThreadLocal其他场景下作用
ThreadLocal另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,我们又需要监听器能够贯穿整个线程的执行过程,这个时候可以怎么做呢?其实就可以采用ThreadLocal,采用ThreadLocal可以让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取到监听器。而如果不采用ThreadLocal,那么我们能想到的可能是如下两种方法:第一种方法是将监听器通过参数的形式在函数调用栈中进行传递,第二种方法就是将监听器作为静态变量供线程访问。上述这两种方法都是有局限性的。第一种方法的问题时当函数调用栈很深的时候,通过函数参数来传递监听器对象这几乎是不可接受的,这会让程序的设计看起来很糟糕。第二种方法是可以接受的,但是这种状态是不具有可扩充性的,比如如果同时有两个线程在执行,那么就需要提供两个静态的监听器对象,如果有10个线程在并发执行呢?提供10个静态的监听器对象?这显然是不可思议的,而采用ThreadLocal每个监听器对象都在自己的线程内部存储,根据就不会有方法2的这种问题。

这里任玉刚大神也在http://blog.csdn.net/singwhatiwanna/article/details/48350919博客中给出了一个例子,正如大神所言

ThreadLocal之所以有这么奇妙的效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值,很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且彼此互不干扰。

MessageQueue

感觉消息队列为啥好说的,主要操作就是增删。值得注意的就是:消息队列的内部实现并不是真正的队列,而是用单链表(明显链表的增删操作易于队列)

这里写图片描述

Looper

looper主要方法:

这里写图片描述

这里看看hongyang大神对Looper两个主要方法prepare(),loop()的分析(截图上是我的理解)

http://blog.csdn.net/lmj623565791/article/details/38377229

这里写图片描述

这里写图片描述

Handler

消息的发送可以用过post或send一系列方法来实现,而post的一系列方法最终是通过send一系列方法来实现的。

1
2
3
4
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

Handler中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int what) //不需要自行new message,推荐
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)

这里写图片描述
这里写图片描述

由上可知,Handler发送消息仅仅是想消息队列插入了一条消息。MessQueue的next方法会返回这条消息给Looper,Looper经过取消息将取得的消息交给Handler处理,即

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //在post消息中,我们进行了new Runnable操
handleCallback(msg); //作,走这个分支
} else { //直接在创建Handle的构造函数中就进行了new Callback操作
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

可见我们在创建Handler实例时有两种方法
这里写图片描述

未完待更新

参考来源:《Android开发艺术探索》
http://blog.csdn.net/lmj623565791/article/details/38377229
http://blog.csdn.net/thanklife/article/details/17006865