android 系统当中,我们都知道不能把请求网络的线程放在主线程中,或者,任何耗时的操作都不应该在主线程中进行,所以,那些耗时的操作都被放在了子线程中进行。因此,android 官方提出了一种主副线程的交互机制(handler)。
关于handler的使用方法,大家去百度上搜下就可以了。本文主要说下handler机制的原理。
大家都知道,handler属于主副线程交互(其实不然,线程自己也可以给自己做交互)。我大致总结handler机制:hanlder机制主要由以下几个重要的类构成:
1.handler。
2.Looper。
3.Message
4.和MessageQueue
这四个类基本组成了handler机制,大致描述为:hanlder负责发送,分发和处理消息机制,是整个消息的负责人,Looper类是一个消息泵,完成消息循环,不断的从MessageQueue中获取属于这个handler的Message,handler获取到对应的Message后,再进行Message的处理。
接下来,我从源代码里面,挑出几个比较重点的部分来阐述这个机制的原理:
1.handler的创建:
public Handler(Looper looper) {
//初始化构建消息系统参数
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
public Handler() {
//初始化构建消息系统参数
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
}
平时使用handler的时候,就有两种方式:一种是所谓的传入Looper,一种是空构造。如果只是做主副线程交互的话,你会发现,好像两种方式都是差不多的。其实看了上述源代码之后,你就会明白,如果默认空构造,Looper也可以自动生成。从handler的创建上,我们明白了,一个handler必定要对应一个Looper,因为handler要得到MessageQueue的引用,而MesssageQueue又是放在Looper中的。
那么,Looper.myLooper的函数又长什么样子呢?
public static Looper myLooper() {
return sThreadLocal.get();
}
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
//存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
可以看到,Looper.myLooper其实也返回了一个Looper实例,这里ThreadLocal类,应该是这样一种解释: ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说无法获取到数据。对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。
如果大家不清楚,可以仔细查看这篇博客http://blog.csdn.net/singwhatiwanna/article/details/48350919(任主席的技术博客)
我们在主线程的handler创建的时候,也喜欢用Looper.getMainLooper(),这个方法的源代码是什么呢?
public synchronized static final Looper getMainLooper() {
return mMainLooper;
}
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
// other codes...
}
mainLooper 是专门属于主线程的Looper,可以看到,创建方式其实和myLoopper是一样的,只是换了一个名字而已。
所以,总结起来来说,hanlder创建的最主要问题,就是找到自己对应的Looper,handler方可正常运行。
2.消息发送和消息循环:
handler发送消息有两种方式:sendMessage和post,这两种方式一种是同步的,一种异步的。hanlder发送了消息了之后,就会把message对象加入message队列中。
final boolean enqueueMessage(Message msg, long when) {
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
}
else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
}
……
}
接下来,我们的Looper就可以进行消息的循环,调用Looper.loop()方法:
public static void loop() {
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
//派发消息 到target(Handler)
msg.target.dispatchMessage(msg);
//回收Msg到msgPool
msg.recycle();
}
}
}
可以看到,hanlder负责发送Message(进入队列的操作),然后Looper不断地从MessageQueue中取出Message(出队列的操作),一进一出,正好解决问题。
3.消息处理
上述代码中:msg.target.dispatchMessage(msg),这条语句其实就是已经开始分发消息了。我们来看看分发消息的源代码:
public void dispatchMessage(Message msg) {
//首先判断runnable对象
if (msg.callback != null) {
handleCallback(msg);
}
else {
//整个消息系统的回调函数 可以不用实现自己Handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//消息处理 通常交给Handler派生类
handleMessage(msg);
}
}
dispatchMessage方法主要做两件事情:1.判定提交的Message是不是Runnable,也就是是不是异步处理的2.如果不是异步的处理的,就是同步处理消息,调用handleMessage(msg),所以大家明白了开发的时候.3如果是异步处理,handleCallback(msg)。
所以大家现在从源代码的角度,可以看到handler到底是一个什么样的存在了吧。下面给出handler的图解,帮助大家总结handler的原理:
其实,我经常觉得hanlder是在自发自收。但是正式这样一种机制,实现线程之间的交互。
————————-Q & A —————————————
Q:既然说handler是自发自收,那线程之间的交互如何而来?
A:每个hanlder都属于一个线程,比如有主线程的handler0,子线程1的handler1,子线程2的handler2,我们需要子线程1给主线程传递消息,那么,在子线程1创建的时候,传入handler作为参数就可以了。
Q: hanlder 机制的消息传递是共享内存吗?
A: 可以这样说,其实,looper是通过ThreadLocal得来的,前面也说过了,ThreadLocal就是线程内部数据存储的类,每个线程都只能得到自己Looper,得到了Looper,就得到了MessageQueue,所以,消息传递机制应该是共享内存实现的。
Q:一般使用子线程给主线程发送消息,那主线程怎么给子线程发送消息呢?
A:简单,在子线程中,创建hanlder,在主线程中得到handler,然后通过这个hanlder就可以实现主线程发送消息给子线程了。可以查看这篇链接:http://www.bkjia.com/Androidjc/893149.html,讲了包括线程怎么自己给自己交互的方式。
Q:子线的handler可以空构造吗?
A: 不可以,子线程必须这样写:
public void run(){
Looper.prepare();
mHandlerTest1=new HandlerTest1(Looper.myLooper());
Message message = new Message();
message.obj = "子线程发送的消息Hi~Hi";
mHandlerTest1.sendMessage(message);
Looper.loop();
}
Q: HandlerThread 是什么东东?
A:上一个问题说了,子线程中的handler是不可以空构造的,必须通过上面Answer中的方式来,其实我们也可以通过HandlerThread来,andlerThread 继承自Thread,内部封装了Looper。我们也可以使用这个类来构造子线程的Looper。
创建一个HandlerThread,即创建了一个包含Looper的线程。
HandlerThread handlerThread = new HandlerThread(“leochin.com”);
handlerThread.start(); //创建HandlerThread后一定要记得start()
获取HandlerThread的Looper
Looper looper = handlerThread.getLooper();
创建Handler,通过Looper初始化
Handler handler = new Handler(looper);