handler机制
一个handler在被new出来的时候,会从当前所在的线程当中取出一个Looper,之后通过这个handler发送的消息,会被加入到这个looper所持有的messageQueue,Looper会在执行.loop()方法之后开启一个死循环,不断从这个messageQueue中取出message来执行。
而我们Android的ui线程,也就是activityThread在main方法中就会开启一个Looper。调用Looper的prepareMainLooper()
方法,该方法会继续调用prepare()方法,而prepare方法则会new出来一个新的looper对象,存入当前线程当中。之后,会new一个handler作为mainThreadHandler来处理所有的ui线程中的事件。
而在looper的死循环中,有一个重要的调用:msg.target.dispatchMessage(msg);
其中msg指的就是使用handler发送的消息,而msg.target则是发送这条消息的handler本身,在调用handler.sendMessage(message)
的时候都会将最终要发送的message消息的target指向自己。
public void dispatchMessage(Message msg) {
//如果callback不为空,说明发送消息的时候是post一个Runnable对象
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//这个是用来拦截消息的
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最终调用我们重写的handleMessage方法
}
}
最后在dispatchMessage中,msg.callback其实是在handler.post(runnable)
的时候被赋值的,通过一个getPostMessage(runnable)
方法将runnable对象赋予msg.callback。所以平时我们执行View.post 这样的方法,就是通过这种方式将runnable交给了ui线程当中的handler去执行,从而实现异步调度。
自己在子线程中创建Looper的这种需求,比如说想在子线程中弹toast,那么可以先Looper.prepare 然后弹一个吐司,最后调用Looper.loop就可以弹出来了。本质原因是toast操作的是window。所以吐司其实是跟activity平级的存在,不受限于子线程中不可以更新ui的限制。一般需要在ui线程中调用吐司,只是因为吐司需要通过Looper中的messageQueue队列来实现排队显而已,而子线程默认是没有looper的。