Handler在经常用在线程间通信上,是Android中常用的异步消息处理机制。线程通信通常是主线程与子线程通信或者子线程之间互相通信,主线程与子线程之间通信又包括主线程向子线程发消息和子线程向主线程发消息。常用的写法如下:
通常我们在子线程中进行耗时操作,比如耗时的计算、网络操作等,得到的结果通常需要展示到UI,但是Android子线程更新UI不安全,所以需要将结果发送给UI线程进行展示。这时就可以使用Handler解决。主要分两步
private Handler mHandler = new Handler (){
@Override
public void handleMessage(Message msg) {
// 收到子线程的消息,进行UI的更新
switch (msg.what){
case 0:
mTextView.setText ((String)msg.obj);
Log.d (TAG, "handleMessage: normal message");
break;
default:
break;
}
}
};
findViewById (R.id.button).setOnClickListener (new View.OnClickListener () {
@Override
public void onClick(View v) {
new Thread (new Runnable (){
@Override
public void run() {
// 子线程发消息
Message message = mHandler.obtainMessage ();
message.what = 0;
message.obj = "子线程更新UI";
mHandler.sendMessage (message);
}
}).start ();
}
});
首先在主线程new一个Handler
并重写handleMessage(Message msg)
方法,子线程中发送来的消息就在这里处理。子线程中通过这个Handler
拿到一个Message对象,让该对象携带要发送的信息,并通过这个Handler
发出去,于是Handler
收到消息,进行更新UI等操作。
这里有一个runOnUIThread(Runnable action)
也能实现,它其实也是基于Handler
的。
如果是子线程处理主线程的消息,就需要在该子线程中定义一个Handler
,并使用该Handler
在主线程中发消息。
// 子线程Handler
private Handler mOtherHandler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
new Thread (new Runnable (){
@Override
public void run() {
Looper.prepare ();
mOtherHandler = new Handler (){
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
Log.d (TAG, "handleMessage: " + (String)msg.obj);
}
}
};
Looper.loop ();
}
}).start ();
}
findViewById (R.id.button2).setOnClickListener (new View.OnClickListener () {
@Override
public void onClick(View v) {
if(mOtherHandler != null){
Message message = mOtherHandler.obtainMessage ();
message.what = 1;
mOtherHandler.sendMessage (message);
}
}
});
同样是两个步骤,主线程发消息,子线程处理消息。唯一不同的是子线程中实例化Handler
之前需要调用Looper.prepare ()
方法,实例化之后需要调用Looper.loop ()
方法,这个后面我们会进行简单分析。
这个就和第二点基本一样了,唯一不同就是发消息是在子线程中。
整个过程如上图,Handler
发送消息到消息队列中,而Looper
则负责从队列中取出消息,并通过Handler
的dispatchMessage(Message msg)
方法,而在该方法中会调用handlerMessage(Message msg)
方法,从而实现了线程间的通信。
Handler的一系列send方法最终调用sendMessageAtTime(Message msg, long uptimeMillis)
方法,我们看一下该方法
它会调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
方法
最终调用MessageQueue#enqueueMessage(Message , long)
方法
这个方法很长,但是其实就是做了一件事,把msg加进队列中。MessageQueue内部维护一个Message对象mMessage,而每一个Message对象内部维护一个名为next的Message对象,指向它的下一个Message。
红框中逻辑表示如果这个mMessage是空的的话,那么相当于把msg放在队列的头部,同时唤醒队列。如下图
紫框中的逻辑则实现了将msg放进队列中,它会遍历队列中的message,直到找到一个message它的next为空,然后把这个msg设置为它的next,并把msg的next设置为空,便于下一次加进来消息。如下图
至此,完成了msg加进队列的操作。
消息放入队列中,需要有人去处理消息,这里就要用到Looper
Looper内部维护一个MessageQueue,而遍历这个队列的方法就是Looper.loop()方法。如下
这个方法也很长,但是省去log和时间计算,它其实就做了一件事,轮询队列处理消息,如果消息处理没了,就是queue.next()
为空了,就跳出循环,如果消息存在,就调用它的target的dispatchMessage(Message msg)
。
查看Message,其内部维护了一个名为target的Handler对象
就是调用了Handler的dispatchMessage(Message msg)方法。找到这个方法,跟进去:
这个handleMessage(msg)
就很眼熟了,这不就是我们new出来的handler实例,然后重写的那个方法嘛!到这里,我们就完成了这个异步消息的处理。
这里其实有一个问题,Looper是如何拿到消息队列的?其实我们在子线程使用Handler时调用了Looper.prepare()
方法,
到这里就明白了为什么这两个方法一定要调用了。
但是为什么主线程使用时就没有调用呢?而且也没有出问题?这时我们需要到ActivityThread中找答案了
Android作为java语言开发的工程,肯定会有一个main方法,这个方法位于ActivityThread.java文件中,是整个Android工程入口方法。UI线程作为贯穿app生命周期的线程,肯定需要在工程启动时就开始配置了。果然在main方法中找到了prepare()方法和loop()方法。所以我们在主线程使用Handler就不用配置了。