一,前言
众多周知, Android 只允许在主线程中更新UI,因此主线程也称为UI线程(ActivityThread)。
如此设计原因有二:
(1) 由于UI操作的方法都不是线程安全的,如果多个线程都可以更新UI,会造成UI界面混乱。
(2)UI操作都加锁,线程同步的话,非常影响性能。
故Android干脆只允许在单个线程-->主线程中操作UI, 否则会报android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建该view的线程才可以改变该view的状态。
Google 为上述单一线程更新UI设计了Handler消息机制,主要场景是为了更新UI,更广泛的应用场景是线程之间进行通信。
二, Handler消息机制
Handler 消息机制主要包含三个重要角色:Handler, MessageQueue, Looper
Handler 发送消息,压入Messagequeue,Looper 作为轮询器,不停地从MessageQueue中获取Message,并再次分发dispatchMessage给Handler, 由handler的handleMessage()方法处理。
1, Handler 消息发送处理者
Handler持有一个Looper对象和一个MessageQueue对象(即looper.mQueue), 在创建Handler对象时可以通过指定Looper(如果不指定,默认为当前线程的Looper对象 ),来确定Handler为哪个目标线程服务: 发送消息到该目标线程,并在目标线程里处理消息。
Handler提供了多种发送消息的方法,它们之间的调用关系如下
在post(Runnable r) / postDelayed(Runnable r, long delay) / sendMessage(msg)里调用的是sendMessageDelayed(msg, delay ) ,进而sendMessgaeAtTime(msg, SystemClock.uptimeMillis()+ delay)。
其中post方法是将runnable 通过getPostMessageRunnable(r) 转化为一个Message对象,msg.callback = r;
最终通过enqueueMessage()将消息压入队列中。可以看到此处将message.target 指定为该handler,(后面会讲target的作用),然后调用了MessageQueue.enquqeueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
另外Handler提供了可以将消息发送到队头的特殊方法, 这样可以使得消息可以优先被处理,postAtFrontOfQueue(msg)、 sendMessageAtFront|OfQueue(msg), 其实本质也一样,只不过消息的时间戳设为0, 最终是sendMessgaeAtTime(msg, 0)
2, Looper 轮询者
Looper作为轮询器,提供的都是静态方法。
(1) Looper.prepare() 初始化
内部创建一个Looper对象,并存在ThreadLocal里。ThreadLocal是线程的数据隔离区,可以存放各个线程单独的数据,存取ThreadLocal数据,不受多线程影响。 new Looper()时,looper对象持有一个新建的MessageQueue对象 和 当前线程对象(Thead.currentThread())。sThreadLocal.set(new Looper() ) ;
(2) Looper.loop() 循环获取messageQueue里的消息
里面有一个死循环for(;;) 去调用 messageQueue.next() 方法获得队头的msg, 然后分发给handler去处理。loop()方法是一个无限循环,之后的代码不会再执行。
(3)Looper.getMainLooper() 获取UI线程的Looper对象
Looper.myLooper()获取当前线程的Looper对象
主线程的looper对象早在ActivityThread启动的时候通过prepareMainLooper()创建好了,因此每次直接获得即可。而获得当前线程的looper对象也很简单, 直接取sThreadLocal存放的looper对象,与prepare()相呼应,sThreadLocal.get()。
3, MessageQueue消息队列
虽然看起来MessageQueue只是一个链表存储的消息容器,但是真正的处理方法都封装在这个类里, 发送消息最终调用的是enqueueMessage(), 轮询消息调用的是next()。这里也巧妙的使用了生产者消费者模式。
enqueueMessage(msg,uptimeMillis)方法 :将消息压入队列,每个消息有时间戳,, msg.when = uptimeMillis;按照uptimeMillis的大小从小到大依次排序插入队列中,uptimeMillis相等的,则先入队列的先处理。
next()方法:获取队列的头部message,即链表的header,值得注意的是,next()方法里也有一个死循环,直到取到满足条件的message才跳出循环,这个条件就是message的执行时间要小于当前时间,message.when < now。如果msg.when <= now, 则不阻塞,直接取出,这不必说, 如果msg.when > now, 就进到下一轮循环里, 直到msg.when <= now.
4, Message消息
一个数据model 本来没什么要说的,但是它有自己的神奇之处。它所能携带的信息自不必说,what唯一标示消息种类,arg1,arg2用于携带简单的数字,obj携带对象类数据, setData(Bundle)携带更复杂的数据。
在Message里有一个实例变量target,target是个handler对象,非常关键, 轮询获取到msg后, msg自调target的dispatchMessage()方法, 继而回调handleMessage()方法处理消息。 这里要注意的是处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。
源码结构示意图(可放大查看)
要点:
1,一个线程有且仅有一个Looper和一个MessageQueue!!! 但是可以有多个Handler, 一个msg到底由哪个handler对象来处理,会通过msg.target 来决定
2, Looper.loop()方法一定要在prepare()之后调用,先初始化了才能调,并且 Looper.loop() 之后的代码不能执行,因为是个死循环for(;;) {mQueue.next(); 源码可以看到 ActivityThread的main方法里最后一句就是loop()方法; 我们在自己实现子线程的消息通信时,一定要记得这点。(当然HandlerThread推荐给你,本质是一个封装好Looper的Thread子类)
3,MessageQueue里有很多native方法,最主要的两个方法next()和enqueueMessage(),mQueue.next() 方法里也有一个死循环,只有获取到一个有效的msg才跳出循环。
6,处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。 其中post(r), getPostMessage(r)就将r作为msg的callback发出去一个msg。 这里肯定优先执行r,而不是handleMessge()方法。
三, HandlerThread
本质是一个封装好了Looper的Thread子类。HandlerThread extends Thread , 持有一个成员变量mLooper,提供有getLooper()方法,其中在Thread的run() 方法中代()码如下,很简洁,可以看到Looper.loop()方法是最后一行。
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}复制代码
使用HandlerThread的方法
HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessge() {
}
};
handler. sendMessage(msg);
注意: handler的创建必须在HandlerThread.复制代码
Handler的创建必须在handlerThread.start()之后,由源码中可以看到Looper的初始化是在handlerThread的run()方法里!!