Android開發中經常使用Handler來實現「跨越線程(Activity)更新UI」。本文將從源代碼角度回答:爲何使用Handler能夠跨線程更新UI?爲何跨線程更新UI必定要用Handler?html
如下這個Demo全然是爲了演示「跨線程更新UI」而寫的。java
界面上僅僅有一個TextView和一個Button。按下Button建立一個後臺線程。該後臺線程每隔一秒更新一次TextView。連續更新10次。結束。android
Activity的代碼例如如下:app
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; } }
佈局文件較爲簡單:less
<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控件。async
如下再展現一個非典型使用方法。不過爲了後面的分析方便。ide
本例是爲了分析方便而建立的;使用一個線程LooperThread來模擬Activity。函數
後面闡述爲何要這麼作,代碼例如如下:oop
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.java
package 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的佈局文件很是easy。就不貼出來了。佈局
以Demo2爲例,這個Demo至少涉及三個線程:GodActivity線程。ActivityThread線程(模擬UI)。匿名線程(GodActivity建立的。叫他aThread)。暫且把GodActivity當作上帝。把ActivityThread看作Demo1裏的Activity。現在,咱們先預覽一下爲何aThread可以經過Handler來更新ActivityThread的UI(純屬虛構)。這兩個線程的交互關係例如如下圖所看到的:
(PS:此前的版本號畫了很是多對象的生命線。結果很是混亂,刪了一堆可有可無的以後,立馬清晰了,^_^)
這個序列圖(Sequence Diagram)已經簡潔明瞭地給出了答案:
如下將從Android 4.4.4源代碼的角度分析Handler的「幕後黑手」。
Demo2中和Handler有關的類除了MessageQueue還有Message和Looper,這幾個類的關係例如如下:
關鍵點:
接下來的代碼,僅僅貼代碼片斷(方法),假設對各種的屬性有所疑惑。可以回頭查看此圖。
依據Looper的凝視可以看到,Looper線程「三部曲」:
如下逐漸切入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
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
首先看上面調用的默認構造方法:
/** * 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僅僅能有一種處理方式。
對於一個後臺線程,它要發出消息(Handler.sendMessage)。對於Activity線程,它要獲得其它線程發來的消息(MessageQueue.next);而這兩種工做都是以MessageQueue爲基礎的。如下。分別分析發送和接收的詳細流程:
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
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.java
Handler.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 插到隊列中間。MessageQueue.java一般咱們沒必要喚醒 // 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; }
前文的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線程纔是真正的「幕後英雄」。
Demo2中的ActivityThread全然是虛構出來的,如下來看看Android的Activity到底是不是想我虛構的那樣有一個Looper。
通過上面的分析,可以從雙方面驗證:
第二點很是easy驗證。僅僅需在隨意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的。
細心的朋友可能會發現。上面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
如下從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.cpp
void 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
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 noneLooper.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分析完成。
接下來看看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.cpp
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
但這是怎樣喚醒等待的線程的呢?猜測:等待線程一定經過epoll_wait等在mEpollFd上,稍後將獲得驗證。
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.cpp
void 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之做喚醒通知之用,並不關心實際內容。
剩下的兩個native方法的實現都很easy,先看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扮演了兩個角色:
本文開頭所給的兩個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仍是很是有必要的。