一、Android 消息机制概述
Android 消息机制是指 Handler 的运行机制,Handler 的运行需要底层的 MessageQueue (消息队列)和 Looper(消息循环) 的支撑。Handler 主要是将一个任务切换到某个指定的线程中去执行。
二、Handler 的使用栗子
public class MainActivity extends Activity implements Button.OnClickListener {
private TextView statusTextView;
//uiHandler在主线程中创建,所以自动绑定主线程的 Looper
private Handler uiHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
statusTextView = (TextView)findViewById(R.id.statusTextView);
Button btnDownload = (Button)findViewById(R.id.btnDownload);
btnDownload.setOnClickListener(this);
System.out.println("Main thread id " + Thread.currentThread().getId());
}
@Override
public void onClick(View v) {
DownloadThread downloadThread = new DownloadThread();
downloadThread.start();
}
class DownloadThread extends Thread{
@Override
public void run() {
try{
System.out.println("DownloadThread id " + Thread.currentThread().getId());
System.out.println("开始下载文件");
//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程
Thread.sleep(5000);
System.out.println("文件下载完成");
//文件下载完成后更新UI
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Runnable thread id " + Thread.currentThread().getId());
MainActivity.this.statusTextView.setText("文件下载完成");
}
};
uiHandler.post(runnable);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
Handler 是在创建的时候绑定实例化所在的线程的 Looper,所以当 Handler 发送消息的时候,所绑定的 Looper 就会在绑定的线程中取消息,从而将任务切换到制定的线程中执行。
三、Handler 机制分析
- 1. ThreadLocal:一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定的线程才能获取存储的数据,对于其他线程来说则无法获取到数据。
举个栗子:
public class MainActivity extends AppCompatActivity {
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBooleanThreadLocal.set(true);
Log.i("TAG","[MainThread] 的值:"+mBooleanThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run() {
mBooleanThreadLocal.set(false);
Log.i("TAG","[Thread#1] 的值:"+mBooleanThreadLocal.get());
}
}.start();
new Thread("Thread#2"){
@Override
public void run() {
Log.i("TAG","[Thread#2] 的值:"+mBooleanThreadLocal.get());
}
}.start();
}
}
打印结果:
04-06 08:30:01.403 2824-2824/com.innovator.handlertest I/TAG: [MainThread] 的值:true
04-06 08:30:01.408 2824-2849/com.innovator.handlertest I/TAG: [Thread#1] 的值:false
04-06 08:30:01.411 2824-2850/com.innovator.handlertest I/TAG: [Thread#2] 的值:null
可以看到在哪个线程设置了值后,在对应线程取出的数据不受其他线程的影响。原因就是:不同线程访问同一个 ThreadLocal 对象的 get()
,ThreadLocal 内部会从各自的线程中取出一个数组,然后在从数组中根据当前 ThreadLocal 的索引去查找对应的 value 值。显然不同线程的数组是不同的。
- 2. MessageQueue 的工作原理
Android 中的消息队列是指 MessageQueue,它主要包含两个操作:enqueueMessage(插入消息) 和 next(读取删除消息)。原理主要是单链表的插入和删除操作。
-
3. Looper 工作原理
Looper 在 Android 消息机制中扮演着消息循环的角色,具体来说就是它会不停地从 MessageQueue 中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
我们都知道 Handler 工作需要 Looper,没有 Looper 的线程就会报错。创建 Looper 的方法如下:
new Thread("Thread#3"){ @Override public void run() { // 在子线程中创建 Looper Looper.prepare(); Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { Log.i("TAG","Runnable 运行在:"+Thread.currentThread().getName()); } }; handler.post(runnable); Looper.loop(); Looper.myLooper().quit(); } }.start();
打印结果:
可见 Runnable 是运行在 Handler 实例化的线程,Looper.loop();
是一个死循环操作,一直阻塞读取消息,所以如果在子线程中使用 Looper,记得要在处理完所有事情后调用 quit()
终止消息循环,否则子线程就一直处于等待的状态。
-
4. Handler 工作原理
Handler 的工作包括发送和接收两个过程,消息的发送可以通过
post()
或者sendMessage()
来实现,这个过程其实是将一个消息插入队列,然后 MessageQueue 的next()
就会返回这条消息给 Looper,Looper 收到消息后就开始处理,最终消息由 Looper 交由 Handler 处理,即 Handler 的dispatchMessage()
被调用。
Handler 的 diapatchMessage()
:
public void dispatchMessage(Message msg){
if(msg.callback != null){
handleCallback(msg);
}else{
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
首先检查 Message 的 callback 是否为 null,不为 null 就通过 handleCallback(msg);
来处理消息。 Message 的 callback 是一个 Runnable 对象,就是 Handler 的 post()
的参数,其实就是执行这里面的 run()
方法的内容。
其次就是检查 mCallBack 是否为空,不为空就调用 mCallBack 的 handleMessage()
。
所以在实例化 Handler 的时候有两种构造方法:
- 使用 Callback 创建 Handler 实例,但不派生 Handler 的子类
mMainHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
return false;
}
});
- 派生 Handler 的子类
mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
四、Handler、Looper、MessageQueue的关系
Looper 负责的就是创建一个 MessageQueue,然后进入一个无限循环体。在这个循环体中不断从该 MessageQueue 中读取消息,而消息的创建者就是一个或多个 Handler 。
Looper 的 prepare():
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 保证了一个线程只有一个 Looper,一个 MessageQueue
sThreadLocal.set(new Looper(true));
}
这里可以看到 Looper 里面有一个 ThreadLocal 的成员变量,所以 Looper 能保证每个线程存放的 Looper 对象都不一样。
Looper 的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
可以看到 Looper 实例化了一个 MessageQueue 对象并保存了当前的线程对象。到这里我们就更能确定 Looper 只与绑定的线程有关,这也是为了后面 Handler 能将任务切换到指定的线程做铺垫。
具体的流程:
1、首先 Looper.prepare()
在本线程中保存一个 Looper 实例,然后该实例中保存一个 MessageQueue 对象;因为 Looper.prepare()
在一个线程中只能调用一次,所以 MessageQueue 在一个线程中只会存在一个。
2、Looper.loop()
会让当前线程进入一个无限循环,不断从MessageQueue 的实例中读取消息,然后回调 msg.target.dispatchMessage(msg)
方法。
3、Handler 的构造方法,会首先得到当前线程中保存的 Looper 实例,进而与 Looper 实例中的 MessageQueue 相关联。
4、Handler 的sendMessage()
方法,会给 msg 的 target 赋值为
handler 自身,然后加入 MessageQueue 中。
5、在构造 Handler 实例时,我们会重写 handleMessage() 方法,也就是 msg.target.dispatchMessage(msg)
最终调用的方法。
看完这么多分析后,我可以推断我之前想到的一个问题了:一个线程能拥有多个 Handler 吗?这些 Handler 会互相干扰吗?
答案:一个线程可以拥有多个 Handler,并且这些 Handler 都是共用同一个线程的 Looper 和 MessageQueue,而且不会互相干扰,因为 Looper 取消息后的回调是 dispatchMessage()
,这里的 msg.target
是之前发送消息的那个 Handler。
五、参考资料
- Android 开发艺术探索
-
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系