Android的消息機制
消息驅動模式是android提供的用來更新UI的一套機制,即消息處理的機制,類似於Windows的消息機制,所有的外部來的按鍵消息、觸屏消息、各種系統Intent、廣播等都轉化爲內部消息,然後在主線程中分發處理。在Windows的消息處理模型中,存在一個系統消息隊列,這個隊列是整個進程的核心,幾乎所有的動作都要轉化成消息,然後放在隊列中,消息的處理只能在主線程中完成。
Android的消息處理則不太一樣。Android沒有全局的消息隊列,消息隊列是和某個線程關聯在一起的。每個線程最多有一個消息隊列,消息的取出和處理在線程中完成。
爲什麼要是用消息機制
Android在一個程序啓動後會創建一個主線程,也叫UI線程(非線程安全)這個線程主要負責監聽屏幕點擊事件與界面繪製,不可以直接在子線程修改UI。之所以設計成單線程模型的UI主線程,是因爲多個線程訪問UI可能會導致衝突,造成界面顯示錯亂,例如,子線程A和子線程B同時修改一個組件的尺寸和背景等資源的情況,如果非要用多線程同步加鎖機制更新UI又會導致性能下降。Android在設計的時候,提供了一種異步回調機制,在子線程中用Handler通知UI線程顯示、更新UI。同時,Android系統也會將大部分耗時的任務(網絡訪問,訪問數據庫)交給子線程處理,當子線程完成任務將通過Handler將結果回傳給UI線程,顯示任務的結果。這種機制同時避免的ANR(Application Not Responding),即應用無響應。
消息機制涉及的類
Looper : 消息循環,Looper內部有一個消息隊列MessageQueue,默認情況下一個線程不包含一個消息循環,需要自己去創建,調用Looper.prepare()創建一個消息循環,調用Looper.Looper()執行這個循環,Android啓動時,爲主線程(UI線程)創建了一個Looper對象。
Message :消息,定義一個包含描述和任意數據對象的消息發送給一個Handler,包含兩個int數據區域和一個對象數據區域。消息的創建最好用Message.obtain()或Handler.obtainMessage() 方法,從一個消息池中回收。
MessageQueue :消息隊列,持有一個Looper分發的消息列表,Message不是直接添加到消息隊列裏的,是添加到Handler對象綁定的Looper的對象中。
Handler : 發送和處理消息(Message和Runnable對象),與一個線程的MessageQueue關聯。一個Handler實例與一個線程(包括線程的消息隊列)綁定。當一個Handler實例被創建時,該實例與創建它的線程/線程的消息隊列 綁定到一起,然後向該消息隊列發送Message和Runnable,並且當發送的Message和Runnable從消息隊列返回時處理它們。
Looper類
主要成員和方法
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
public static void prepare()
public static void prepareMainLooper()
public static Looper getMainLooper()
public static void loop()
}
prepare()
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
每一個線程只能有一個Looper類的實例對象,Looper類的實例必須通過prepare()方法來創建。prepare()方法會創建一個Looper對象,並把它保存在靜態變量mTreadLocal中,一個線程多次調用prepare()會拋出異常。
靜態變量sThreadLocal
該變量是一個ThreadLocal類型,即線程本地存儲區(TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。它通過將需要保存的對象和線程id關聯在一起的方式實現了線程本地存儲功能,作用就是將Looper類線程隔離,保證每個線程只能有一個Looper對象。
ThreadLocal.set(T value):
public void set(T value) { Thread currentThread = Thread.currentThread(); //獲取當前線程 Values values = values(currentThread); //查找當前線程的本地儲存區 if (values == null) { //當線程本地存儲區,尚未存儲該線程相關信息時,則創建Values對象 values = initializeValues(currentThread); } //保存數據value到當前線程this values.put(this, value); }
ThreadLocal.get():
public T get() { Thread currentThread = Thread.currentThread(); //獲取當前線程 Values values = values(currentThread); //查找當前線程的本地儲存區 if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; //返回當前線程儲存區中的數據 } } else { //創建Values對象 values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); //從目標線程存儲區沒有查詢是則返回null }
loop()
public static void loop() {
final Looper me = myLooper();//獲取本地存儲區的Looper對象
……
final MessageQueue queue = me.mQueue;//消息隊列
……
for (;;) {
Message msg = queue.next(); // might block,這裏可能阻塞
if (msg == null) {
return;//沒有消息退出
}
……
msg.target.dispatchMessage(msg);//消息分發
……
msg.recycleUnchecked();//消息回收
}
}
loop()是一個靜態方法,其中有一個無限的for循環,loop()方法會循環從消息隊列MessageQueue中取出消息,然後分發出去。消息的分發是通過Message中的target變量完成的,這個變量是Handler類型的,一個Looper對象可以對應多個Handler對象。
Message類
Message是消息的載體。Message設計成了Parelable類的派生類,表明Message可以通過binder來跨進程發送。
public final class Message implements Parcelable
消息的類型
成員 | 類型 | 註解 |
---|---|---|
what | int | 消息類別 |
when | long | 消息觸發時間 |
arg1 | int | 參數1 |
arg2 | int | 參數2 |
obj | Object | 消息內容 |
target | Handler | 消息相應的地方 |
callback | Runnable | 回調方法 |
obtain()
public static Message obtain() {//從消息池中返回一個新的Message實例,避免申請太多的Message
synchronized (sPoolSync) {//消息池中有從消息池中取出
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
recycle()
public void recycle() {//回收消息
if (isInUse()) { //判斷消息是否正在使用
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it is still in use.");
}
return;
}
recycleUnchecked();//回收沒有使用的消息
}
MessageQueue類
消息的構造
MessageQueue對象的構造是調用本地方法nativeInit()完成的。nativeInit()創建了一個本地NativeMessageQueue,它本質上是一個代理類。它把Java層的調用轉變爲native層Looper類的函數調用,native層的Looper實現了一整套完整的消息處理機制。但是Java層的Looper類和native層的Looper類並沒有直接的關係。MessageQueue雖然使用了Native層的Looper類,但只用到了等待/喚醒機制,其餘的如消息隊列的實現還是在Java層。
消息的處理
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//調用本地方法等待nextPollTimeoutMillis秒,-1表示永遠阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
//針對this對象同步,只要next方法沒退出,再調用本對象的任何方法都將導致調用線程掛起
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. 忽略普通消息,查找下一條異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//如果還沒有處理這條消息的時間,計算等待時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false; //取消阻塞的標誌
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//如果退出標誌設置了,銷燬native對象
if (mQuitting) {
dispose();
return null;
}
//檢查是否安裝了idle handler
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
//如果又idle handler ,循環繼續,而不是阻塞
nextPollTimeoutMillis = 0;
}
}
向MessageQueue發消息
使用的是enqueueMessage()方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//如果消息的target爲NULL,拋出異常
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {//如果加入的是正在處理的消息對象,拋出異常
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {//用this對象同步
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;//p指向消息隊列頭
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg; //把消息插到隊列頭
needWake = mBlocked;//這時如果處理線程阻塞了,則需要喚醒
} else {
//如果設置了“SyncBarrier”,只有插入了“異步消息”才需要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//如果已經一條“異步消息”在隊列裏了,而且在本條消息前處理,而不需要喚醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {//如果需要喚醒,則喚醒線程
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()方法插入消息時根據時間來排序,時間早的插在前面。消息隊列的組織利用了Message類裏面的next指針形成一個從頭指向尾的單向鏈表。插入時計算是否需要喚醒處理。enqueueMessage()方法會盡量避免喚醒處理線程,只有插入了一條馬上要處理的消息,或者在暫停處理消息的情況下,有插入了“異步消息”的情況下才會去喚醒處理線程。其餘的情況都是把消息放到隊列的中部或尾部(時間未到)。如果前面還有消息沒處理,這條消息就更不急於處理了。
Hander類
構造Handler
構造一個Handler對象需要兩個參數,線程的Looper對象和消息的處理函數。Looper是必須的,構造方法不指定則使用當前線程的Looper對象。但是callback不是必須的,可以用callback實現對消息的集中處理,也可以把處理消息的callback方法放在消息對象中。
無參構造
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//匿名類、內部類或本地類都必須申明爲static,否則會警告可能出現內存泄露
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //從sThreadLocal中獲取Looper對象,下面Looper中的函數可以看到
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //消息隊列,來自Looper對象
mCallback = callback; //回調方法
mAsynchronous = async; //設置消息是否爲異步處理方式
}
//Looper類中的方法,見2.3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
有參構造
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
send方法
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }
public final boolean sendEmptyMessage(int what){ return sendEmptyMessageDelayed(what, 0); }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
……
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
……
return enqueueMessage(queue, msg, uptimeMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
……
return enqueueMessage(queue, msg, 0);
}
所有的send消息都調用了enqueueMessage()方法,最終調用了MessageQueue中的enqueueMessage()方法,就是把消息加入到了消息隊列,並指定執行的時間。可以看到下面queue.enqueueMessage()方法中除了msg,只有時間。上述一系列的send方法只是爲了方便使用,可以從方法名中看出各自的使用情景。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
post方法
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r){
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
從代碼上看這些post方法的實現也是用send,只是多了Runnable對象,然後調用getPostMessage()方法獲取一個Message對象來發送。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;//這裏callback被攜帶在message中,參見3.1
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);//message中的callback
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//Handler構造時,指定的callback
return;
}
}
handleMessage(msg);//Handler默認方法
}
}
從上面可以看到消息分發的優先級:
- Message的回調方法:message.callback;
- Handler的回調方法:Handler.mCallback.handleMessage(msg);
- Handler的默認方法:Handler.handleMessage(msg)。
dispatchMessage()方法的調用在Looper類中的loop()方法:
public static void loop() {//見2.4 for (;;) { …… msg.target.dispatchMessage(msg);//消息分發 …… msg.recycleUnchecked();//消息回收 } }
流程圖
參考文獻
[1] http://gityuan.com/2015/12/26/handler-message-framework/
[2] http://yuqiangqiang.com/2014/11/08/android基礎總結/9-android下的Handler機制/
[3] http://www.kancloud.cn/digest/androidfrom-0/144443
[4] https://developer.android.com/training/multiple-threads/communicate-ui.html
[5] https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/
[6] http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html#!comments
[7] http://www.jianshu.com/p/02962454adf7
[8] http://www.feeyan.cn/?p=17
[9] http://anany.me/2015/04/12/handler/
[10] http://gityuan.com/2015/12/27/handler-message-native/#nativepollonce