# Android中的消息机制
TIP
本节内容:
一.什么是Handler;
二.Handler、Looper和MessageQueue的基本原理;
三.通过Handler实现线程间的通信;
四.初步分析Handler的工作原理;
# 一.什么是Handler?
Handler实际上就是一个处理器,在Android官方文档中并未对其有过精确的定义,
但我们可以将Handler、Looper和MessageQueue看作成一个整体,
其构成的这个整体就是Android中最重要的消息传递和消息处理机制。
在程序中,Handler是个类,所在的包结构:android.os.Handler;这里的包名os指的是:operation syster,即操作系统。
# 二.Handler、Looper和MessageQueue的基本原理;
Handler不断的将消息对象放入到消息队列里面,
Looper不断的向消息队列里面取消息对象(如果消息队列里面没有消息对象的话,Looper就处于等待的状态),取出后再交给Handler来处理。
如下图所示:
# 三.通过Handler实现线程间的通信;
# 1.在主线程当中定义一个类继承Handler并实现handlerMessage()方法;
# 2.在Worker Thread中通过Handler发送消息。
即在线程的run()
方法中创建消息对象并使用Handler发送该对象。代码如下:
Message msg = handler.obtainMessage();
msg.属性 = 要赋得值;例如:msg.obj=””;
2
handler.sendMessage(msg);
这句代码相当于做了四件事情:
WARNING
(1)这句代码将消息对象首先发送(放置)到消息队列当中;
(2)Looper将会从消息队列当中将消息对象取出;
(3)Looper将会找到与消息对象对应的Handler对象;
(4)Looper将会调用Handler对象的handlerMessage(Message msg)方法,用于处理消息对象。
注意:sendMessage()
方法无论是在主线程当中还是在工作线程当中发送都是可以的。
这样就解决了工作线程与主线程之间的通信问题。
# 四.初步分析Handler的工作原理;
结合上面的代码案例及Android源码,
# 1.handler.obtainMessage()
这句代码的工作是这样的:
(1)首先在obtainMessage()
方法中执行obtain(this)
这句代码,
即:调用了obtain(Handler handler)
方法,所传参数this
指的是当前的Handler对象。
(2)在obtain(Handler handler)
方法中首先调用了无参的obtain()
方法,该方法创建了一个Message对象并返出,
因此在obtain(Handler handler)
方法中得到了一个Message对象,然后将传进来的参数(也就是当前的Handler对象)赋值给这个Message对象的target
属性。
这样就在Handler
与Message
之间建立了对应关系,这种对应关系是一对多的关系,
即:一个Handler
可以生成多个Message
对象。
在这里总结为一句话:使用Handler
生成Message
,所生成的Message
对象的target
属性就是该Handler
对象。
# 2.要想在Worker Thread中使用Looper的方法,
WARNING
首先调用Looper.prepare()
方法;
然后生成一个Handler对象,调用其构造方法:
mHandler = new Handler(){
public void handlerMessage(Message msg){
//这里处理传入的消息
}
};
2
3
4
5
然后再调用Looper.loop()
方法。
(1)先从Looper.prepare()
方法说起,这个方法就是
建了一个Looper
对象作为值、以当前线程为键然后存入线程本地变量sThreadLocal
(即:ThreadLocal
)
,
这样就将Looper
与Thread
建立了对应关系(一个线程只能有一个Looper
对象)。
而在Looper
对象的构造方法中又创建了一个MessageQueue
对象赋值给成员变量mQueue
,并取得当前线程的对象赋值给成员变量mThread
,
(这样就将MessageQueue
与Thread
建立了对应关系);
(2)然后在Handler
的构造方法中调用Looper.myLooper()
方法根据线程本地变量sThreadLocal
取出了Looper
对象赋值给Handler
的成员变量mLooper
,该对象又取出了其属性mQueue
(也就是MessageQueue
对象)赋值给了Handler
的成员变量mQueue
。
截止到现在,Looper
、MessageQueue
、Handler
三者之间已建立起了一一对应的关系,
即一个Handler
对应一个Looper
对象,一个Looper
对应一个MessageQueue
对象。
(3)调用Looper.loop()
方法从消息队列中循环的向外取出数据。
该方法中,首先获取到消息队列的对象mQueue
。然后执行for(;;)
循环。
该方法中的for(;;)
同while(true)
循环一样,都是死循环,直到遇到break
才停止。
在这个循环中,首先取Message对象(通过MessageQueue
对象的next()
方法)并赋值给局部变量msg
,如果没有就处于阻塞状态,
如果有且Message对象不为null
的时候,调用msg.target.dispatchMessage(msg)
方法并将当前的消息对象作为参数传递,因为msg.target
得到的是Handler
对象,那么这里其实是调用了Handler的dispatchMessage(msg)
方法,在该方法中又调用了handlerMessage(Message msg)
方法来处理消息对象。
# 补充: 关于ThreadLocal;
ThreadLocal是java当中跟线程有关的一个类,严格意义上来讲,应将它叫做线程本地变量,
那么什么叫做线程本地变量?其实就是保存与当前线程相对应的值的变量。
ThreadLocal可以想象成是一个特殊的HashMap,只不过HashMap的键值都是obj对象,而ThreadLocal的键则是线程对象。
当执行ThreadLocal对象的set(Object obj)方法时,
将会存入一个以当前线程对象为键、以所传入参数为值的键值对。
执行ThreadLocal对象的get()方法(该方法无参),
将会根据当前线程对象为键,取出与之对应的值。
也就是说,当执行set(Object obj)方法时,实际上就是向ThreadLocal当中添加一个键值对。
键 | 值 |
---|---|
线程4 | 值4 |
线程3 | 值3 |
线程2 | 值2 |
线程1 | 值1 |