本篇博客介绍Handler与Looper的关系,可能大家都有用过Handler实现跨线程的通信(比如子线程处理完耗时,使用handler发送消息给主线程进行UI更新操作),但是里面的原理大家都清楚吗?本篇博客将和大家一起了解里面的机制。
Handler
我们首先来看下api的介绍
A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
简单翻译下:Handler可以让我们把Message或者Runnable对象发送到与之关联的消息队列中,每个Handler实例都与单一的线程,以及该线程的消息队列对应。当你创建一个Handler的时候,它就与该线程/创建它的线程中的消息队列进行绑定。从这点来说,它可以传递 message和runnable到消息队列,然后在消息队列中执行任务。
上面加粗部分的介绍,可能是我因为不过关,我总觉得描述的不够准确,后面会有例子说明这点。
Looper
Looper的设计使用了ThreadLocal, 保证了每个线程最多只能有一个对应的looper,通过Looper.prepare(),可以为当前线程绑定一个looper。Looper中持有一个messageQueue, handler发送的消息都会进到这个messageQueue里,然后进行执行。 handler里发送任务的最终的执行线程,就是looper所绑定的线程。
我们直接上测试代码吧
Looper looperThread1;
private void testHandle() {
new Thread(new Runnable(){
@Override
public void run() {
Log.d("Tag",
Thread.currentThread()+"Looper.prepare");
Looper.prepare();
looperThread1 = Looper.myLooper();
Looper.loop();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("Tag", Thread.currentThread()+"Create Handler");
Handler handler = new Handler(looperThread1);
handler.post(new Runnable() {
@Override
public void run() {
Log.d("Tag", Thread.currentThread()+"handle runnable");
}
});
}
}).start();
}
运行的结果是:
D/Tag: Thread[Thread-146,5,main]Looper.prepare
D/Tag: Thread[Thread-147,5,main]Create Handler
D/Tag: Thread[Thread-146,5,main]handle runnable
这段代码证明了,Handler中发送的message或runnable对象,最终执行的时候,并非一定是在 Handler的创建线程,而是在创建Handler时,在构造方法里传入的 looper所在的线程,只是我们调用无参构造方法时,默认用的是当前线程looper.
如果大家还有不理解的地方可以可以再看看源码,或者留言一起学习讨论。