消息机制
1、作用:跨线程通信
2、场景:当子线程进行耗时操作以后需要更新UI时,通过Handler将有关的UI操作切换到主线程中执行
系统不建议在子线程访问UI的原因:
1、UI线程非安全,在多线程中并发访问可能会导致UI控件处于不可预期的状态。
2、如果加上锁的话,会导致UI控件变得复杂和低效。同时会阻塞某些进程的运行
关键要素:
- Message(消息):需要被传递的消息,由MessageQueue统一列队,最终由Handler处理
- MessageQueue(消息队列):存放Message,内部通过单链表的数据结构来维护消息列表,Message可能来自不同的Handler
- Handler:负责Message的处理和发送,包含着Looper和MessageQueue
- Looper(消息泵):通过Looper.loopa()不断的从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者,可以有多个的Handler
- Thread(线程):负责调度整个消息循环,即消息循环的执行场所
3、具体的实现方式
- 在主线程实例化一个全局的Handler对象;
- 在需要执行UI操作的子线程里实例化一个Message并填充必要数据,调用Handler.sendMessage(Message msg)方法发送出去;
- 复写handleMessage()方法,对不同Message执行相关操作;
private Handler handler = new Handler(){
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case xxx:
...
updateUI();
break;
}
}
};
....
private void threadMethod() {
new Thread(){
@Override public void run() {
super.run();
//创建Message的三种方法
//1、Message msg = new Message();
//2、Message msg2 = Message.obtain();
//3、Message msg1 = handler1.obtainMessage();
handler.sendMessage(msg);
}
}.start();
}
上面的Message的创建有三种方法,最好使用第二种和第三种,这样做的好处是避免重复创建Message对象。具体详见:Message的创建方式:new Message和obtainMessage的区别
下面就是在子线程中创建使用Handler
class LooperThread extends Thread{
private Handler handler;
public void run(){
Looper.prepare();//这里面就是ThreadLocal.set(Looper);
handler = new Handler(){
public void handleMessage(){
// 处理消息
}
}
}
}
从上面的两个例子,我们可以看到通过handler的sendMessage方法来发送消息,通过Looper.loop方法进行消息循环。同时子线程里面要new一个Handler的时候,就必须在前面加上Looper.prepare()方法。至于消息的处理是隐藏在Loop和MessageQueue中,我们接下来会一点点的深入分析。
注意:我们为什么在新的线程里面,首先要Looper.prepar,而在主线程里面不需要。这是因为,我们主线程在程序入口的main方法里面已经调用了Looper.prepar。
ThreadLocal类是用来提供线程内部的局部变量。这些变量在多线程的环境下访问(通过get或者set方法访问)时能保证各个线程里的变量相对独立于其他的线程变量。
这玩意并不是为了解决多线程的访问共享变量,而是为了每个线程创建一个单独的变量副本
4、源码的分析
先看看Handler机制简图吧,这个是引用别人的图片
具体的源码分析,可以参考博客Android的消息机制分析
参考文章:
深入剖析ThreadLocal
android的消息处理机制(图+源码分析)——Looper,Handler,Message
Android的消息机制分析