前言
在 Android 编程当中,一般是不可以在子线程中更新主线程的 UI ,这时候 Android 给我们提供了一套在子线程中更新 UI 的消息机制,即 Handler 消息处理机制。
Handler 允许你发送进程消息和可运行的对象到关联的线程消息队列中。每个 Handler 的实例关联一个线程和这个线程的消息队列。当创建一个 Handler 的时候,系统会自动把这个 Handler 实例绑定到当前创建这个 Handler 的线程中。
Handler的基本使用(示例)
[1.0] 在主线程中创建一个 Handler 的实例,并重写 Handler 的 handleMessage(Message msg) 方法
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText((String)msg.obj);
}
};
[2.0] 在主线程中创建一个子线程,并通过 Handler 的实例调用 sendMessage 给主线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "aaa";
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(message);
}
}).start();
上面代码功能是实现一个启动应用程序 3 秒钟或更新 TextView 控件的内容。下面进行 Handler 的源码分析,Handler 是如何实现上面的逻辑的:也就是 Handler 通过 sendMessage(message) 把一个 Message 实例如何发送给对应的 Handler 的 handleMessage(Message msg) 的
:
示例源码分析
下面先从 Message 的入口,也就是 sendMessage(Message msg)
方法开始分析。通过查看 sedMessage(Message msg)
的方法源码,可知道最后会调用到 sendMessageAtTime(Message msg, long uptimeMillis)
方法:
1 | [源码 1] |
上面的 [源码 1],最重要的需要明白注释 1 处的 mQueue
是来自哪里的消息队列 MessageQueue
,也就是在 Handler 的类中,它是如何初始化拿到实例的。因为在上面的示例源码中的 Handler 实例重写的 handleMessage(Message msg)
是被 dispatchMessage(Message msg)
中调用的,如下面的 [源码 2] 的注释 1 处:
1 | [源码 2] |
上面的 dispatchMessage(Message msg)
方法的主要的作用是处理系统的消息的,也可以看作分发系统的消息。好的,我们的重点不在这里,那么是谁调用了 dispatchMessage(Message msg)
这个方法呢?通过查看源码我们会发现:
1 | [源码 3] |
在上面的 [源码 3] 中的注释 1 处调用了 dispatchMessage(Message msg)
方法。在注释 2 处,通过调用 myLooper()
获取当前线程的 Looper 对象,而该 Looper 对象携带着当前线程的消息队列(注意:是当前创建 Handler 的线程),而在注释 3 处,for死循环主要的作用是把 queue
这个消息队列里面的 Message
消息取出,然后通过 msg.target.dispatchMessage(msg)
把消息分发出去,也就是调用了 [源码 2] 的 dispatchMessage(msg)
方法。阅读到这里的你,可能心里面有个咕嘟:为什么通过上面 [源码 3] 的注释 2 处调用的 myLooper()
方法就可以获取到当前线程的消息队列呢?
好的,在 Android 系统源码里面最精彩的部分:在 Handler 的构造函数里面:
1 | [源码 4] |
在上面的 [源码 4]中的注释 1 处,可以看到,也是同样地获取当前线程的 Looper 实例 mLooper,然后在注释 3 处,把 final 类型的消息队列实例赋给 mQueue
这个全局变量。到了这里,在 Handler 的构造函数里面初始化了消息队列 MessageQueue
,因此,在 [源码 1] 中的 mQueue
便是当前线程 Looper 对象所持有的消息队列:
1 | [源码 1] |
到了这里,[源码 1] 里面调用的 enqueueMessage(queue, msg, uptimeMillis)
方法,其逻辑如上面的 [源码 5],把需要发送的 Message 发送给了 Looper.myLooper().mQueue 这个消息队列(MessageQueue 是利用类似于单链表的方式, 以 Message 为节点来存储 msg 的,即以链表的方式构造队列);
阅读到这里,你可能会比较疑惑,为什么可以直接通过 Looper.myLooper()
方法获取到 Looper 的实例呢? 其实,Android 应用程序的 Activity
实例是由 ActivityThread
创建的,同时,ActivityThread
也会默认去创建主线程,也会创建主线程的 Looper
的实例。
小结
对上面源码的总结,主要就是: Handler 负责把子线程中传进来的消息(Message),传递给 Looper,由 Looper 把消息封装成消息队列,然后再有 Handler 从消息队列中把消息分发到子线程外面。