Android訊息機制——深入理解Handler
Handler是什麼
Handler允許向一個執行緒的訊息佇列傳送和處理Message和Runnable兩種物件。每一個Handler例項與一個執行緒和其訊息佇列關聯。當你新建一個Handler時,它會與建立它的執行緒和這個執行緒的訊息佇列繫結,從這時起,Handler就可以分發訊息和任務到訊息佇列以及從訊息佇列中執行訊息和任務。
Handler主要有兩個用法:
- 定時訊息或任務,分發在某一時刻執行的訊息或任務
- 將一個動作放入佇列中,但是這個動作的執行在不是本執行緒中
當一個應用程序建立後,主執行緒會執行一個訊息佇列負責頂層應用物件(activities,broadcast receivers,等等)和任何建立的窗體。你也可以建立自己的執行緒,然後通過Handler來與主執行緒通訊。
Handler的使用
常用情景——更新UI
Handler的一個重要使用場景就是在主執行緒中宣告一個Handler物件,然後在一個執行緒中執行費時操作,執行完之後需要更新UI。但是我們知道非UI執行緒是不可以更新UI的,此時就可以通過Handler來發送一個訊息,這樣主執行緒中的Handler就可以接受到這個訊息然後更新UI了。
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//Handler收到了非UI執行緒的訊息後,更新UI,因為這個Handler是在主執行緒中的
case 1:
showTv.setText("Hello,Handler");
break;
default:
break;
}
}
};
private TextView showTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showTv = (TextView) findViewById(R.id.tv_show);
doWork();
}
/**
* Handler使用場景1——更新UI
* 開啟一個執行緒執行耗時操作,執行完之後,通過Handler傳送一個訊息
*
*/
private void doWork() {
new Thread(new Runnable() {
@Override
public void run() {
//模擬耗時操作
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);
}
}).start();
}
}
上面的程式碼,doWork模擬耗時操作,是在非UI執行緒中執行的,執行後之後,handler傳送了一個訊息出去,當handler收到這個訊息時就可以更新TextView了。執行程式可以發現,在程式啟動後10s,文字由“Hello World”變成了“Hello Handler”。
定時任務
所謂定時任務,就是設定傳送訊息的時間,這個時間可以是延遲一定時間得到相對時間或設定絕對時間。常見的場景有應用啟動的閃屏,比如說3s後進行主介面,就可以設定3s之後再發送訊息;還有倒計時的實現,比如遊戲計時器。下面就實現一個簡單的計時器。程式碼如下:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//Handler收到了計時器中的訊息
case 2:
initVal--;
if (initVal < 0)
break;
timerTv.setText(initVal + "s");
//下一秒繼續計時
startTimer();
break;
default:
break;
}
}
};
private TextView timerTv;
private int initVal = 60;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timerTv = (TextView) findViewById(R.id.tv_timer);
timerTv.setText(initVal + "s");
startTimer();
}
/**
* 通過Handler的定時訊息實現計時器
*/
private void startTimer() {
//1s之後的訊息
handler.sendEmptyMessageDelayed(2, 1000);
}
從上面的程式碼可以看到startTimer方法中呼叫的是sendEmptyMessageDelayed方法,使用的是相對時間;而如果要使用絕對時間的話,可以使用sendEmptyMessageAtTime方法。上面的程式碼實現的效果就是一個1分鐘倒計時器,1s鍾發一次訊息,進行一次更新。
效果如下:
非主執行緒中Handler的使用
上面兩個例子都展示的是主執行緒中的Handler是如何使用的。在前面說到,Handler是可以用於任何執行緒的,不一定就非得是主執行緒。至於在非UI執行緒中使用,比起主執行緒中使用,稍微有點複雜,得遵循以下的格式:
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//非主執行緒使用Handler
initHandler();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
otherThread();
}
//非UI執行緒初始化Handler
private void initHandler() {
new Thread(new Runnable() {
@Override
public void run() {
//Step 1:呼叫Looper.prepare()
Looper.prepare();
//Step 2:例項化Handler,重寫handleMessage方法
handler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i("MainActivity", "Other Thread Completed");
break;
}
}
};
//Step 3:呼叫Looper.loop()
Looper.loop();
}
}).start();
}
private void otherThread() {
new Thread(new Runnable() {
@Override
public void run() {
//模擬耗時任務
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//傳送訊息
handler2.sendEmptyMessage(1);
}
}).start();
}
上面的程式碼中首先在initHandler中的一個執行緒中初始化Handler例項,步驟有三步:
- 呼叫Looper.prepare()方法
- 例項化Handler
- 呼叫Looper.loop()方法
缺少第一步將會丟擲異常;缺少第二步,別的執行緒就無法使用Handler傳送訊息了;缺少第三步,Handler就不會收到訊息也就不可以處理訊息了。至於為什麼是這樣,下面分析原始碼時會講到。
而otherThread方法中還是模擬一個耗時任務,在任務完成後發訊息給Handler,而Handler收到訊息後就是列印Log資訊,具體如下:
11-24 06:27:04.084 1488-1512/system_process I/ActivityManager: Displayed com.xks.handlerdemo/.MainActivity: +4s884ms
11-24 06:27:13.062 27218-27267/com.xks.handlerdemo I/MainActivity: Other Thread Completed
從時間可以看到,從介面顯示到Log列印,期間差不多相隔10s。
分析完Handler的使用之後,下面將從原始碼的角度解釋Handler的原理。
Handler原始碼分析
Handler使用步驟分析
在上面關於Handler的使用部分,講到了在非主執行緒中使用Handler的三個步驟,下面我們就以非主執行緒中使用Handler為例來開始原始碼分析之旅。
Loop.prepare()
Looper類用來給一個執行緒執行訊息迴圈。執行緒預設是沒有關聯的訊息迴圈的;為了建立一個訊息迴圈,需要在一個執行緒中呼叫prepare方法來執行這個迴圈,loop方法會使它不斷地處理訊息,除非迴圈停止。
public static void prepare() {
prepare(true);
}
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));
}
上面的程式碼有一個sThreadLocal變數,這是一個ThreadLocl型別的變數,儲存每個執行緒的Looper變數。從上面的程式碼可以看到每個執行緒只能有一個Looper。最後新建一個Looper物件,然後儲存在sThreadLocal中。下面看Looper的構造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
從上面的程式碼可以看出,Looper的構造方法例項化了MessageQueue物件,儲存了執行緒變數。至此,Looper與執行緒關聯了並且建立好了MessageQueue物件,MessageQueue物件是儲存訊息的一個佇列,後面會具體講到。
看完了第一步之後,我們再看第二步,Handler的例項化。
Handler例項化
Handler有很多個過載的構造方法,如下:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
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());
}
}
//從sThreadLocal中根據本執行緒得到Looper
mLooper = Looper.myLooper();
//如果本執行緒沒有Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
從上面的程式碼可以看出,Handler的初始化主要涉及三個值:mLooper、mQueue、mCallback和mAsynchronous。從上面的程式碼可以看到,所有的構造方法最終都是呼叫後面兩個構造方法,一個外部不提供Looper,一個外部提供Looper。先看外部提供Looper的,直接就是賦值,在上面第一步提到了,Looper.prepare()之後,訊息佇列也建立好了。
對於外部不提供Looper的情況,則需要呼叫Looper.myLooper()獲取Looper,myLooper方法如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
該方法從sThreadLocal中根據執行緒取出Looper.prepare()中存入的Looper物件。得到了Looper,也就得到了MessageQueue。
Callback引數是一個處理訊息的回撥,async引數設定訊息佇列中的每個訊息是否非同步。
分析完上面Handler的構造方法後,我們知道了如果不傳入Looper物件,那麼將會使用本執行緒的Looper。這也就解釋了在非UI執行緒中需要首先呼叫Looper.prepare(),因為在那裡建立了Looper物件並儲存在了sThreadLocal中;那麼問題來了:
在UI執行緒中初始化的Handler,它的Looper是在哪裡賦值的?
主執行緒中Looper的例項化
對於這個問題,我們看應用的主執行緒ActivityThread中的main方法:
public static void main(String[] args) {
//與本文無關的程式碼
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
從上面的程式碼我們可以看到,首先呼叫prepareMainLooper方法,然後得到一個sMainThreadHandler,最後呼叫Looper.loop方法,好像也是3步。下面就看下這三步到底做了些什麼?
Looper.prepareMainLooper()方法
public static void prepareMainLooper() {
//等同於Looper.prepare()
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
從上面的程式碼可以看到,首先呼叫prepare(false)方法,這和顯式的呼叫Looper.prepare()是同樣的效果,這句執行完後,主執行緒中的Looper就建立好了並且被儲存在了sThreadLocal中了。接下來,判斷sMainLooper這個變數是否為null,丟擲異常,這個異常和prepare()方法中丟擲的異常有些類似,都說明每個執行緒只能有一個Looper,最後是呼叫myLooper方法將從sThreadLocal中取出的Looper賦值給sMainLooper物件。所以,至此,主執行緒的Looper建立成功,並且其值儲存在Looper中的sMainLooper變數中。
如果想使用這個Looper,可以呼叫getMainLooper方法得到這個變數:
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
所以,可以看到主執行緒中的prepareMainLooper方法除了與prepare()方法同等作用後,還將Looper儲存了下來,這樣非UI執行緒也是可以使用的。
Handler的例項化
如果sMainThreadHandler為null,那麼呼叫getHandler方法獲取,下面是該方法的實現:
final Handler getHandler() {
return mH;
}
從程式碼可以看出,返回值是一個Handler。那麼mH是個什麼呢?
final H mH = new H();
從上述程式碼可以看到mH是ActivityThread的一個欄位,並且直接初始化了。而H是ActivtyThread的一個巢狀類,如下:
private class H extends Handler {
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
//還有很多case
...
}
}
}
從上面可以看到,H繼承自Handler並重寫了handleMessage方法,這個H主要負責應用Activity、Broadcast等的訊息傳遞。
至此,主執行緒也完成了第二步操作。
Looper.loop()方法
在主執行緒的最後,可以看到呼叫了Looper.loop()方法。所以說,在主執行緒中使用Handler也遵循了那三步。從分析主執行緒的Handler建立中我們發現了,一個執行緒對應一個Looper,一個Looper物件一個MessageQueue,但是一個執行緒裡可以有多個處理訊息和傳送訊息的Handler。 這個後面會詳細講解。
Looper.loop()方法
接下來,我們分析最後一步,Looper.loop()方法,該方法用於執行迴圈。程式碼如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//從訊息佇列中取訊息
Message msg = queue.next(); // might block
//如果停止了
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
//分發訊息
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
從上面的程式碼可以看到,首先獲取Looper物件和MessageQueue物件,然後進入死迴圈,嘗試從訊息佇列中取訊息,如果可以取到訊息,則會呼叫msg.target.dispatchMessage方法進行訊息的分發。
從上面可以看到,loop方法之後才會從訊息佇列中取訊息,也就是說如果不呼叫loop方法,那麼儘管可以往訊息佇列中發訊息,但是卻因為不取走訊息,所以也就沒有處理了。
總結
經過上面的分析後,我們可以看出每一步的作用:
1. Looper.prepare()方法建立Looper以及MessageQueue
- Handler例項化後,其與Looper與MessageQueue關聯;可以通過Handler傳送訊息以及重寫handleMessage方法編寫處理邏輯
- Looper.loop()方法後開始從訊息佇列中取訊息給Handler處理。
Handler傳送訊息、處理訊息和刪除訊息
在理解了Looper、Handler、MessageQueue三者的關係之後,我們再看Handler傳送訊息和處理訊息的原理。
在看Handler傳送訊息之前,先看一下接下來要打交通的Message類。
Message類
Message可以用來描述訊息和包含資料,Handler傳送的都是這個物件。主要有如下幾個欄位:
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid = -1;
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
/*package*/ Message next;
從上面可以看到,欄位主要分為兩類,一類公有的,包括what、arg1、arg2、obj和replyTo;一類訪問為包許可權,target、callback和next等。從這可以看出Message是一個連結串列中的節點,MessageQueue是一個單鏈表結構的佇列。
訊息物件池
Message類中維持著一個訊息池,引數如下:
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
而如果需要獲取一個Message物件,推薦使用obtain方法而不是構造方法,如下:
public static Message obtain() {
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();
}
上面的方法主要是從物件池中得到訊息,如果物件池為空,那麼就新建一個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();
}
void recycleUnchecked() {
//清除狀態
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//放入物件池中
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
上面的程式碼解釋瞭如何將訊息回收到訊息物件池中的。
看完了Message類後,再來看Handler是如何與Message物件打交通的。
傳送訊息
Handler可以傳送訊息或者Runnbale物件,有很多傳送的方法,還可以設定時間限制,所以其傳送主要有兩個引數,一個訊息,一個時間。下面看一個方法:
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
//得到訊息物件
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//得到訊息佇列物件
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
很多傳送方法,比如sendEmptyMessage、sendEmptyMessageDelayed等最終都會進入這個方法。從上面的程式碼可以看到,最終呼叫了enqueueMessage方法將訊息入隊,程式碼如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//賦值target屬性
msg.target = this;
//如果設定了非同步
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//呼叫MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,首先將Message與Handler關聯,因為處理訊息時要根據訊息找到處理該訊息的Handler;設定訊息是否是非同步屬性,最後呼叫MessageQueue鄂enqueueMessage方法將訊息入隊。
下面是MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
//如果Handler為null,異常;因為處理時找不到誰該處理這條訊息
if (msg.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) {
//如果退出了
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;
}
//置Flag
msg.markInUse();
//訊息執行的時間點,絕對時間
msg.when = when;
//佇列的頭結點
Message p = mMessages;
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
從入隊,可以看到MessageQueue內部維持的是一個單鏈表結構的優先佇列,並且其內部的節點是按照執行時間先後順序排序的,也就是隊頭的節點總是最先執行的。下圖展示了插入一個時間節點為5的訊息
上圖中,當插入一個時間節點為5的訊息,那麼將會插入到4和6之間。
上述程式碼就是將訊息放入佇列中,下面分析從訊息中取出訊息。
處理訊息
在分析Looper.loop()方法中,我們知道了從訊息佇列中取訊息發生於此,會呼叫MessageQueue的next方法獲取訊息,next方法如下:
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();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//輪詢訊息,如果有則返回
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//隊頭訊息
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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 {
// 得到一個訊息
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;
}
// 如果退出了
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
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);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
當呼叫了next得到了一個訊息之後,將會呼叫msg.target.dispatchMessage方法。在Message的分析中知道了target就是Handler,下面看Handler的dispatchMessage方法:
public void dispatchMessage(Message msg) {
//如果Message的Callback不為null,即通過postXXX方法發出的訊息
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果Handler的mCallback引數不為null,建立Handler時指定
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//否則呼叫該方法
handleMessage(msg);
}
}
從上面的程式碼可以看到幾種情況,
- 如果Message的callback不為null,那麼呼叫handleCallback方法
- 如果Message的callback為null,且Handler的mCallback不為null,那麼呼叫介面的handleMessage方法,相當於攔截器的作用
- 否則,呼叫Handler自己的handleMessage方法
下面首先看handleCallback處理帶有Runnbale的Message,
private static void handleCallback(Message message) {
message.callback.run();
}
從程式碼中可以看到就是呼叫Runnbale的run方法,此時,需要注意的是,這個任務是在Handler關聯的執行緒中執行的;如果Handler是主執行緒的,那麼這個Runnnbale就是在主執行緒,也就可以進行更新UI操作;如果不是,那麼一定不能更新UI。
下面再看handleMessage方法,程式碼如下:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
handleMessage是一個空實現,這也就是子類為什麼需要重寫該方法的原因,重寫該方法,才能真正處理訊息。
刪除訊息
Handler不止可以發出訊息,還可以對已經發出且未執行的訊息進行取消。可以呼叫removeXXX方法,有如下方法:
public final void removeCallbacks(Runnable r)
{
mQueue.removeMessages(this, r, null);
}
public final void removeCallbacks(Runnable r, Object token)
{
mQueue.removeMessages(this, r, token);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
主要有上面五個方法,可以分別用於刪除提交的任務,what欄位等;而最終都是呼叫MessageQueue的removeMessages方法,下面是其實現:
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
從上面的程式碼可以看出,主要根據Handler、what和object三個欄位判斷一個訊息是否應該被移出佇列,只有當三個條件都滿足時,才會將訊息移出佇列。
如何退出訊息迴圈?
前面的分析中一旦呼叫了loop方法後,就進入了一個死迴圈,這也意味著建立Looper的那個執行緒一直執行著,那麼如何退出訊息迴圈呢?
退出迴圈,可以呼叫Looper的quit和quitSafely方法,如下:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
quit和quitSafely方法的區別在於:
- quit方法不會再執行訊息佇列中的任何訊息
- quitSafely方法會執行訊息佇列中的訊息;但是對於執行時間超過當前時間訊息,則刪除。
下面是MessageQueue的quit方法:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//安全退出
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
可以看到如果是安全退出,那麼會呼叫removeAllFutureMessagesLocked方法,否則呼叫removeAllMessageLocked方法,首先看removeAllMessagesLocked方法刪除所有訊息:
private void removeAllMessagesLock