Android開發中經常使用Handler來實現“跨越線程(Activity)更新UI”。本文將從源碼角度回答:爲什麼使用Handler能夠跨線程更新UI?爲什麼跨線程更新UI一定要用Handler?
Demo
Demo1. 用Handler更新UI
下面這個Demo完全是爲了演示“跨線程更新UI”而寫的。界面上只有一個TextView和一個Button,按下Button創建一個後臺線程,該後臺線程每隔一秒更新一次TextView,連續更新10次,結束。
Activity的代碼如下:
public class MainActivity extends Activity {
static final String TAG = "MainActivity";
Handler handler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text = (TextView)findViewById(R.id.txtHello);
Button button = (Button)findViewById(R.id.btnRun);
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Log.d(TAG, "clicked!");
new Thread() {
public void run() {
for(int i=0; i<10; i++) {
Message msg = new Message();
msg.what = 1;
msg.obj = "item-"+i;
handler.sendMessage(msg);
Log.d(TAG, "sended "+"item-"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
String str = "unknow";
switch(msg.what) {
case 1:
str = (String)msg.obj;
break;
default:
break;
}
Log.d(TAG, "recv " + str);
text.setText(str);
super.handleMessage(msg);
}
};
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
佈局文件較爲簡單:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/txtHello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"
/>
</RelativeLayout>
這裏展示的是Handler的典型用法——用來更新UI控件。
下面再展示一個非典型用法,僅僅是爲了後面的分析方便。
Demo2. 自制ActivityThread模擬Activity
本例是爲了分析方便而創建的;使用一個線程LooperThread來模擬Activity。
後面闡述爲什麼要這麼做,代碼如下:
package com.example.handlerdemo;
import android.os.Bundle;
import android.os.Message;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
static final String TAG = "MainActivity";
ActivityThread acitivityThread = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
}
private void setupViews() {
TextView tv = (TextView)findViewById(R.id.txtHello);
Button bt = (Button)findViewById(R.id.btnStart);
Log.d(TAG, String.format("[MainActivity] Thread %s(%d)",
Thread.currentThread().getName(), Thread.currentThread().getId()));
acitivityThread = new ActivityThread();
acitivityThread.start();
acitivityThread.waitForHandlerReady();
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
for(int i=0; i<10; i++) {
Message msg = new Message();
msg.what = i;
acitivityThread.mHandler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
MainActivity.javapackage com.example.handlerdemo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
public class ActivityThread extends Thread {
static final String TAG = "LooperThread";
public Handler mHandler = null;
public ActivityThread() {
super("LooperThread");
}
@Override
public void run() {
Looper.prepare();
synchronized(this) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, String.format("recv msg.what: %d in Thread: %s(%d)", msg.what,
Thread.currentThread().getName(),Thread.currentThread().getId()));
}
};
this.notify();
}
Looper.loop();
}
public void waitForHandlerReady() {
try {
synchronized(this) {
while(mHandler == null)
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ActivityThread.java
這個Demo的佈局文件很簡單,就不貼出來了。
爲什麼使用Handler能夠跨線程更新UI?
概覽
以Demo2爲例,這個Demo至少涉及三個線程:GodActivity線程,ActivityThread線程(模擬UI),匿名線程(GodActivity創建的,叫他aThread)。暫且把GodActivity當做上帝,把ActivityThread看做Demo1裏的Activity。現在,我們先預覽一下爲什麼aThread可以通過Handler來更新ActivityThread的UI(純屬虛構),這兩個線程的交互關係如下圖所示:
(PS:此前的版本畫了很多對象的生命線,結果很混亂,刪了一堆無關緊要的之後,立刻清晰了,^_^)
這個序列圖(Sequence Diagram)已經簡潔明瞭地給出了答案:
- Activity線程的幕後還有一個MessageQueue;MessageQueue故名思議是一個Message組成的Queue;
- aThread只是將數據以Message的形式掛到了Activity幕後的MessageQueue上了;
- Activity線程從MessageQueue上取Message並調用Handler.handlerMessage,所以實際的“更新動作”還是發生在Activity線程內;
詳解
下面將從Android 4.4.4源碼的角度分析Handler的“幕後黑手”。
幾個關鍵類
Demo2中和Handler有關的類除了MessageQueue還有Message和Looper,這幾個類的關係如下:
關鍵點:
- MessageQueue通過Message.next維護鏈表結構(java引用即指針);
- ActivityThread的消息循環被封裝在Looper.loop()內,Looper.prepare()用於創建屬於當前線程的Looper和MessageQueue;
- 每個Message可以通過target指向一個Handler,Handler實際上就是一個用來處理Message的callback;
接下來的代碼,只貼代碼片段(方法),如果對各類的屬性有所疑惑,可以回頭查看此圖。
Looper.prepare()
根據Looper的註釋可以看到,Looper線程“三部曲”:
- Looper.prepare()
- new Handler() { /* override handleMessage() */ }
- Looper.loop();
下面逐漸切入Looper.prepare():
public static void prepare() {
prepare(true);
}
Looper.java無參數版本調用了有參數版本:
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.java這段代碼中引用了sThreadLocal,它被定義爲ThreadLocal類型,即線程私有數據類型(或者叫做線程級別單例)
ThreadLocal<T>可以理解爲Map<Thread,T>的一層包包裝(實際上Android,JVM都是按Map實現的,感興趣的同學可自行研究;set(value)時,以當前線程對象爲key,所以每個線程能夠保存一份value。)
可見Looper.prepare()調用使得AcitivityThread通過Looper.sThreadLocal<Looper>持有了一個Looper對象。繼續看Looper的構造方法Looper(quitAllowed):
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread(); // 和當前線程關聯
}
Handler.java
可以看到Looper的構造函數中創建了一個MessageQueue。
流程又轉到了MessageQueue的構造函數MessageQueue(quitAllowed):
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue.java
Handler()
首先看上面調用的默認構造方法:
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread. 將當前線程的Looper與此handler關聯。
* 如果當前線程沒有looper,這個handler將不能接收消息,從而導致異常拋出
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
Handler.java
默認構造方法又調用了另一版本的構造方法,如下:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) { // FIND_POTENTIAL_LEAKS 爲 false;
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(); // 獲取當前線程(調用者)的Looper
if (mLooper == null) { // 如果當前線程沒有Looper,則拋異常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 這裏引用的MessageQueue是Looper()中創建的
mCallback = callback;
mAsynchronous = async;
}
Handler.java
Handler()調用了Looper.myLooper():
public static Looper myLooper() {
return sThreadLocal.get(); // 從該線程的“單例”中取出Looper對象
}
Looper.java
Looper.loop()
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// mLatencyLock is only initialized for non USER builds
// (e.g., USERDEBUG and ENG)
if ((!sLatencyEnabled) || (me != sMainLooper)) {
msg.target.dispatchMessage(msg); // 通過msg.target分派消息
}
else { // 記錄性能數據
long t1 = SystemClock.uptimeMillis(); // 獲得當前毫秒數(自啓動)
msg.target.dispatchMessage(msg);
long t2 = SystemClock.uptimeMillis() - t1; // t2就是dispatchMessage(msg)所用時間
if (t2 < 50) {
// We don't care about these from a latency perspective
}
else if (t2 < 250) {
// Fast response that usually has low impact on user experience
sLatencyCountFast++;
sLatencySumFast += t2;
if (sLatencyCountFast >= 100) {
String name = getProcessName();
long avg = sLatencySumFast / sLatencyCountFast;
EventLog.writeEvent(2731, "mainloop2_latency1", name, avg);
sLatencyCountFast = 0;
sLatencySumFast = 0;
}
}
else if (t2 < 1000) {
sLatencyCountSlow++;
sLatencySumSlow += t2;
if (sLatencyCountSlow >= 10) {
String name = getProcessName();
long avg = sLatencySumSlow / sLatencyCountSlow;
EventLog.writeEvent(2731, "mainloop2_latency2", name, avg);
sLatencyCountSlow = 0;
sLatencySumSlow = 0;
}
}
else {
String name = getProcessName();
EventLog.writeEvent(2731, "mainloop2_bad", name, t2);
}
}
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.recycle();
}
}
Looper.java可以看到,Looper.loop()的for循環實際上就是“消息循環”,它負責從消息隊列(MessageQueue)中不斷地取出消息(MessageQueue.next),然後通過msg.target來派發(dispatch)消息。
How to dispatch?
下面看看Message到底是如何被dispatch的: public void dispatchMessage(Message msg) {
if (msg.callback != null) { // 方法 1
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { // 方法 2
return;
}
}
handleMessage(msg); // 方法 3
}
}
Handler.java
從這段代碼可以看出,實現正常的Message處理有三種方式:
- 爲Message.callback註冊一個Runnable實例。
- 爲Handler.mCallback註冊一個Handler.Callback實例。
- 重寫Handler的handleMessage方法。
另外,這三種方法優先級依次降低,且一個Message只能有一種處理方式。
Message的發送與獲取
對於一個後臺線程,它要發出消息(Handler.sendMessage);對於Activity線程,它要得到其他線程發來的消息(MessageQueue.next);而這兩種工作都是以MessageQueue爲基礎的。下面,分別分析發送和接收的具體流程:
Handler.sendMessage()
Demo中後臺線程正是通過Handler.sendMessage實現向Activity發消息的,Handler.sendMessage方法的代碼如下:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
Handler.java public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
Handler.java其中,其中SystemClock.uptimeMillis()返回自啓動以來CPU經過的毫秒數。
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);
}
Handler.javaHandler.enqueMessage其實只是對MessageQueue.enqueueMessage的簡單包裝:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 將當前Handler(通常已重寫handleMessage方法)與該Message綁定(通過target)
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); // 調用MessageQueue.enqueueMessage
}
Handler.java
這裏看到了Looper.loop()裏引用的target的來源。
流程轉到了MessageQueue.enqueueMessage(),看命名基本知道它是入隊操作,代碼如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) { // 臨界區
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages; // 鏈表頭
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// p == null 隊列爲空
// when == 0 由 Handler.sendMessageAtFrontOfQueue() 發出
// when < p.when 新消息的when比隊頭要早
// New head, wake up the event queue if blocked.
msg.next = p; // 將msg放到隊頭,step 1
mMessages = msg; // 將msg放到隊頭,step 2
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 事件(event)隊列,除非隊頭有一個barrier,
// 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插入prev和p之間
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.java根據這段代碼可知,MessageQueue上的Message是按照when大小排列的。唯一可能讓人疑惑的是最後的nativeWake,稍後討論。
MessageQueue.next()
前文的Looper.loop方法通過MessageQueue.next()取出消息,現在看看它是如何實現的:
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// We can assume mPtr != 0 because the loop is obviously still running.
// The looper will not call this method after the loop quits.
nativePollOnce(mPtr, nextPollTimeoutMillis); // 等待通知,可能阻塞
synchronized (this) {
// Try to retrieve the next message. Return if found.
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) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next; // 將msg節點摘下
} else { // prevMsg == null, msg是鏈表頭
mMessages = msg.next;
}
msg.next = null; // msg與MessageQueue“斷絕關係”
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg; // 退出點1 到這爲止,是常規邏輯
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null; // 退出點2
}
// 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("MessageQueue", "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;
}
}
MessageQueue.java
MessageQueue.next()同樣讓人疑惑的是nativePollOnce,稍後也將見分曉。
小結
MessageQueue.next()和MessageQueue.sendMessage()分別被Activity線程、後臺線程調用,而他們兩個線程可能同時在調用這兩個方法,所以他們共享並修改的成員變量需要加鎖,這就是synchronized (this)出現的原因。
至此,已經能夠完整的回答“爲什麼用Handler能夠實現跨線程更新UI”。簡單的說,Activity線程的背後都有一個消息隊列(MessageQueue),後臺線程通過Handler的sendMessage方法向這個消息隊列上放消息;Activity線程將消息從消息隊列上取下來之後,通過具體Handler的handleMessage方法處理消息,而更新UI的代碼就在這個handleMessage中;所以,後臺線程並沒有做實際的“更新”,只是將要更新的內容以藉助MessageQueue告訴了Activity線程,Activity線程纔是實際做“更新”動作的人。
簡言之,Handler並沒有真正的實現“跨線程”更新UI,而是將要更新的數據(Message攜帶)和如何更新(Handler攜帶)通過消息隊列告訴了UI線程,UI線程纔是真正的“幕後英雄”。
真正的ActivityThread
Demo2中的ActivityThread完全是虛構出來的,下面來看看Android的Activity到底是不是想我虛構的那樣有一個Looper。
經過上面的分析,可以從兩方面驗證:
- 看看Activity源碼中執行onCreate之前是否調用了Looper.prepare()。
- 執行onXXX方法時的CallStack上是否有Looper.loop();
第二點很容易驗證,只需在任意onXXX方法中打一個斷點,然後看程序的CallStack,就一面瞭然了:
根據這個調用棧,可以很明顯的看到有Looper.loop;同時還能看到是ActivityThread.main調用它的,所以可以看看ActivityThread.main的源碼:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); // 它和Looper.prepare類似
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread.java
所以,上面提到的兩方面都得到了驗證。即真正的ActivityThread是有Looper的。
Native浮雲
細心的朋友可能會發現,上面MessageQueue的代碼中還遺留幾個native開頭方法:nativeInit,nativePollOnce,nativeWake。
下面就來掃清這些“遮眼”的浮雲。和這幾個native方法直接對應的是:
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake },
{ "nativeIsIdling", "(I)Z", (void*)android_os_MessageQueue_nativeIsIdling }
};
android_os_MessageQueue.cpp
nativeInit
下面從adnroid_os_MessageQueue_nativeInit開始,顧名思義,nativeInit當然是完成一些初始化工作的。
static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); // 創建了NativeMessageQueue
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jint>(nativeMessageQueue);
}
android_os_MessageQueue.cpp看看NativeMessageQueue的聲明:
class NativeMessageQueue : public MessageQueue {
public:
NativeMessageQueue();
virtual ~NativeMessageQueue();
virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
void pollOnce(JNIEnv* env, int timeoutMillis);
void wake();
private:
bool mInCallback;
jthrowable mExceptionObj;
};
android_os_MessageQueue.cpp
NativeMessageQueue繼承了MessageQueue,再來看看MessageQueue的聲明:
class MessageQueue : public RefBase {
public:
/* Gets the message queue's looper. */
inline sp<Looper> getLooper() const {
return mLooper;
}
/* Checks whether the JNI environment has a pending exception.
*
* If an exception occurred, logs it together with the specified message,
* and calls raiseException() to ensure the exception will be raised when
* the callback returns, clears the pending exception from the environment,
* then returns true.
*
* If no exception occurred, returns false.
*/
bool raiseAndClearException(JNIEnv* env, const char* msg);
/* Raises an exception from within a callback function.
* The exception will be rethrown when control returns to the message queue which
* will typically cause the application to crash.
*
* This message can only be called from within a callback function. If it is called
* at any other time, the process will simply be killed.
*
* Does nothing if exception is NULL.
*
* (This method does not take ownership of the exception object reference.
* The caller is responsible for releasing its reference when it is done.)
*/
virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) = 0;
protected:
MessageQueue();
virtual ~MessageQueue();
protected:
sp<Looper> mLooper;
};
android_os_MessageQueue.h現在看看NativeMessageQueue的構造函數:
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
android_os_MessageQueue.cpp
NativeMessageQueue的構造函數又調用了Looper::getForThread(),Looper::Looper()和Looper::setThread(),其中getForThread和setForThread都是靜態函數:
sp<Looper> Looper::getForThread() {
int result = pthread_once(& gTLSOnce, initTLSKey);
LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
return (Looper*)pthread_getspecific(gTLSKey);
}
Looper.cpp
這段代碼中,在第一次執行pthread_once時將調用initTLSKey。
void Looper::initTLSKey() {
int result = pthread_key_create(& gTLSKey, threadDestructor);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
}
Looper.cpp
void Looper::threadDestructor(void *st) {
Looper* const self = static_cast<Looper*>(st);
if (self != NULL) {
self->decStrong((void*)threadDestructor);
}
}
Looper.cppvoid Looper::setForThread(const sp<Looper>& looper) {
sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
if (looper != NULL) {
looper->incStrong((void*)threadDestructor);
}
pthread_setspecific(gTLSKey, looper.get());
if (old != NULL) {
old->decStrong((void*)threadDestructor);
}
}
Looper.cpp
Looper::setForThread和getForThread中分別使用了pthread_setspecific,pthread_getsepcific,pthread_key_create,實現了線程私有的looper引用,這和Java層Looper類似。
Looper的構造函數如下:
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
mIdling = false;
// Allocate the epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT); // 用epoll實現IO多路複用,EPOLL_SIZE_HINT定義爲8
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); // 將Wake管道的讀端添加到mEpollFd上
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}
Looper.cpp從Looper的構造函數可以看到,Looper的Wake是由管道+epoll實現的,且管道的兩端fd都被設置爲NONBLOCK的,並通過epoll實現IO多路複用。Looper的數據成員(data member)聲明如下:
struct Request {
int fd;
int ident;
sp<LooperCallback> callback;
void* data;
};
struct Response {
int events;
Request request;
};
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
const Message& message) : uptime(uptime), handler(handler), message(message) {
}
nsecs_t uptime;
sp<MessageHandler> handler;
Message message;
};
const bool mAllowNonCallbacks; // immutable
int mWakeReadPipeFd; // immutable
int mWakeWritePipeFd; // immutable
Mutex mLock;
Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
bool mSendingMessage; // guarded by mLock
// Whether we are currently waiting for work. Not protected by a lock,
// any use of it is racy anyway.
volatile bool mIdling;
int mEpollFd; // immutable
// Locked list of file descriptor monitoring requests.
KeyedVector<int, Request> mRequests; // guarded by mLock
// This state is only used privately by pollOnce and does not require a lock since
// it runs on a single thread.
Vector<Response> mResponses;
size_t mResponseIndex;
nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
Looper.h
Looper數據成員涉及的類型還有有:作爲callback的LooperCallback,MessageHandler,以及Message:
class MessageHandler : public virtual RefBase {
protected:
virtual ~MessageHandler() { }
public:
/**
* Handles a message.
*/
virtual void handleMessage(const Message& message) = 0;
};
Looper.h
class LooperCallback : public virtual RefBase {
protected:
virtual ~LooperCallback() { }
public:
/**
* Handles a poll event for the given file descriptor.
* It is given the file descriptor it is associated with,
* a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT),
* and the data pointer that was originally supplied.
*
* Implementations should return 1 to continue receiving callbacks, or 0
* to have this file descriptor and callback unregistered from the looper.
*/
virtual int handleEvent(int fd, int events, void* data) = 0;
};
Looper.h
struct Message {
Message() : what(0) { }
Message(int what) : what(what) { }
/* The message type. (interpretation is left up to the handler) */
int what;
};
Looper.h至此,android_os_MessageQueue_nativeInit分析完畢。
nativeWake
接下來看看android_os_MessageQueue_nativeWake和android_os_MessageQueue_nativePollOnce。
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
return nativeMessageQueue->wake();
}
android_os_MessageQueue.cpp
android_os_MessageQueue_nativeWake調用了NativeMessageQueue::wake:
void NativeMessageQueue::wake() {
mLooper->wake();
}
android_os_MessageQueue.cppNativeMessageQueue::wake直接將工作轉交給了Looper::wake:
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1); // 向pipe的寫段寫入一個字節
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
Looper.cpp可以看到nativeWake非常簡單,只是向pipe上寫一個字節。但這是如何喚醒等待的線程的呢?猜想:等待線程必然通過epoll_wait等在mEpollFd上,稍後將得到驗證。
nativePollOnce
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, timeoutMillis); // 調用NativeMessageQueue::pollOnce()
}
android_os_MessageQueue.cpp
android_os_MessageQueue_nativeWake調用了NativeMessageQueue::pollOnce:
void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
mInCallback = true;
mLooper->pollOnce(timeoutMillis);
mInCallback = false;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
android_os_MessageQueue.cpp
NativeMessageQueue::pollOnce調用了Looper::pollOnce:
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
Looper.h
Looper::pollOnce(int)調用了另一版本的Looper::pollOnce:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++); // 取出一個response
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
Looper.cpp
pollOnce的for(;;)循環裏先查看是否還有沒有取出的response,若有,取出一個立即返回;否則,調用Looper::pollInner,poll出一個IO事件(wake通知,後面能夠看到):
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mIdling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // 關鍵!等待wake通知
// No longer idling.
mIdling = false;
// Acquire lock.
mLock.lock();
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error, errno=%d", errno);
result = ALOOPER_POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = ALOOPER_POLL_TIMEOUT;
goto Done;
}
// Handle all events.
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
for (int i = 0; i < eventCount; i++) { // 處理所有事件
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken(); // 調用Looper::awoken(),執行實際的wake通知
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex)); // push到mRequest上
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// Invoke pending message callbacks.調用等待的消息回調
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// Remove the envelope from the list.
// We keep a strong reference to the handler until the call to handleMessage
// finishes. Then we drop it so that the handler can be deleted *before*
// we reacquire our lock.
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
this, handler.get(), message.what);
#endif
handler->handleMessage(message); // 調用Message回調(MessageHandler)
} // release handler
mLock.lock();
mSendingMessage = false;
result = ALOOPER_POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// Invoke all response callbacks.調用所有響應回調
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == ALOOPER_POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
this, response.request.callback.get(), fd, events, data);
#endif
int callbackResult = response.request.callback->handleEvent(fd, events, data); // 調用事件回調(LooperCallback)
if (callbackResult == 0) {
removeFd(fd);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
result = ALOOPER_POLL_CALLBACK;
}
}
return result;
}
Looper.cppvoid Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); // 讀到臨時的buffer,
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
Looper.cpp
Looper::awoken的read從mWakeReadFd上讀出的消息被放在一個臨時的buffer上,這再次表明了這個pipe之作喚醒通知之用,並不關心實際內容。
nativeIsIdling 和 nativeDestroy
剩下的兩個native方法的實現都非常簡單,先看nativeIdling:
static jboolean android_os_MessageQueue_nativeIsIdling(JNIEnv* env, jclass clazz, jint ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
return nativeMessageQueue->getLooper()->isIdling();
}
android_os_MessageQueue.cpp
NativeMessageQueue::getLooper:
inline sp<Looper> getLooper() const {
return mLooper;
}
android_os_MessageQueue.cpp
bool Looper::isIdling() const {
return mIdling;
}
Looper.cpp再看nativeDestroy:
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env);
}
android_os_MessageQueue.cpp
nativeDestroy將nativeMessageQueue的強引用減1,引用計數減爲0時,對象會自動被析構並回收。
小結
隱藏在nativePollOnce和nativeWake背後起着重要作用的其實是pipe。nativeWake向pipe的寫端寫一個字節,通知前臺線程“有消息來了”。
總結
後臺線程使用Handler更新UI的本質上是“生產者消費者問題”。後臺線程扮演生產者,生產消息(Message),並放到消息隊列上;前臺線程扮演消費者,從消息隊列上取消息,並處理(消費)它。
在這個過程中Handler扮演了兩個角色:
- 消息隊列的窗口,後臺線程通過Handler.sendMessage()向消息隊列放消息;
- 處理消息的回調,前臺線程通過Handler.handleMessage()處理從隊列上取下來的消息;
引申
本文開頭所給的兩個Demo都是“單生產者單消費者問題”。
這個問題中需要指出的是,消費者必然唯一。因爲每個線程最多隻能只有一個Looper(通過Looper.prepare創建),而MessageQueue是由Looper的構造方法創建的,所以每個Looper對應一個MessageQueue;所以不可能有多個消費者線程共享一個MessageQueue。
但生產者可以不必唯一,比如本文開頭的Demo1,按下Button之後,會創建一個後臺線程,這個線程每個1秒更新一次TextView,更新10次後結束。當你點下Button後不到10秒(比如5秒)時,再次點下Button,此時又創建了一個後臺線程;這時兩個後臺線程都是生產者。感興趣的朋友可以自己試試,看看實際運行的效果。
pipe是隻有兩個端的結構,多生產者時,有多個線程向寫端write,但始終只有一個線程從讀端read。所以,nativePollOnce可以實現爲阻塞的,即pipe的讀端mWakeReadPipeFd可以不設爲NONBLOCK(當然也就不需要要用epoll了)。但由於可能存在多個生產者,所以pipe的寫端設爲NONBLOCK還是很有必要的。