編輯:關於Android編程
Android開發中經常使用Handler來實現“跨越線程(Activity)更新UI”。本文將從源碼角度回答:為什麼使用Handler能夠跨線程更新UI?為什麼跨線程更新UI一定要用Handler?
下面這個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; } }
布局文件較為簡單:
這裡展示的是Handler的典型用法——用來更新UI控件。
下面再展示一個非典型用法,僅僅是為了後面的分析方便。
本例是為了分析方便而創建的;使用一個線程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.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的布局文件很簡單,就不貼出來了。
以Demo2為例,這個Demo至少涉及三個線程:GodActivity線程,ActivityThread線程(模擬UI),匿名線程(GodActivity創建的,叫他aThread)。暫且把GodActivity當做上帝,把ActivityThread看做Demo1裡的Activity。現在,我們先預覽一下為什麼aThread可以通過Handler來更新ActivityThread的UI(純屬虛構),這兩個線程的交互關系如下圖所示:
(PS:此前的版本畫了很多對象的生命線,結果很混亂,刪了一堆無關緊要的之後,立刻清晰了,^_^)
這個序列圖(Sequence Diagram)已經簡潔明了地給出了答案:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KCkFjdGl2aXR5z9+zzLXExLu687u509DSu7j2TWVzc2FnZVF1ZXVlo7tNZXNzYWdlUXVldWW5ysP7y7zS6crH0ru49k1lc3NhZ2XX6bPJtcRRdWV1ZaO7YVRocmVhZNa7yse9q8r9vt3S1E1lc3NhZ2W1xNDOyr250rW9wctBY3Rpdml0ecS7uvO1xE1lc3NhZ2VRdWV1ZcnPwcuju0FjdGl2aXR5z9+zzLTTTWVzc2FnZVF1ZXVlyc/IoU1lc3NhZ2WyorX308NIYW5kbGVyLmhhbmRsZXJNZXNzYWdlo6zL+dLUyrW8yrXEobC4/NDCtq/X96Gxu7nKx7eiyfrU2kFjdGl2aXR5z9+zzMTao7sKPHA+PGJyPgo8L3A+CjxoMj7P6r3iPC9oMj4KPHA+z8LD5r2rtNNBbmRyb2lkIDQuNC401LTC67XEvce2yLfWzvZIYW5kbGVytcShsMS7uvO62srWobGhozwvcD4KPGgzPry4uPa52Lz8wOA8L2gzPgo8cD5EZW1vMtbQus1IYW5kbGVy09C52LXEwOCz/cHLTWVzc2FnZVF1ZXVlu7nT0E1lc3NhZ2W6zUxvb3BlcqOs1eK8uLj2wOC1xLnYz7XI58/Co7o8L3A+CjxwPjxpbWcgc3JjPQ=="/uploadfile/Collfiles/20140829/2014082909073994.jpg" alt="\">
關鍵點:
接下來的代碼,只貼代碼片段(方法),如果對各類的屬性有所疑惑,可以回頭查看此圖。
根據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
ThreadLocal
繼續看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.callback注冊一個Runnable實例。為Handler.mCallback注冊一個Handler.Callback實例。重寫Handler的handleMessage方法。另外,這三種方法優先級依次降低,且一個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
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
前文的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; // 到這為止,是主體邏輯 } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. 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("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()和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。
經過上面的分析,可以從兩方面驗證:
看看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("ActivityThread.java"); 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是有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_castandroid_os_MessageQueue.cpp(nativeMessageQueue); }
看看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 spandroid_os_MessageQueue.hgetLooper() 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 mLooper; };
現在看看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都是靜態函數:
spLooper.cppLooper::getForThread() { int result = pthread_once(& gTLSOnce, initTLSKey); LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed"); return (Looper*)pthread_getspecific(gTLSKey); }
這段代碼中,在第一次執行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_castLooper.cpp(st); if (self != NULL) { self->decStrong((void*)threadDestructor); } }
void Looper::setForThread(const spLooper.cpp& looper) { sp 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::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; spLooper.hcallback; void* data; }; struct Response { int events; Request request; }; struct MessageEnvelope { MessageEnvelope() : uptime(0) { } MessageEnvelope(nsecs_t uptime, const sp handler, const Message& message) : uptime(uptime), handler(handler), message(message) { } nsecs_t uptime; sp handler; Message message; }; const bool mAllowNonCallbacks; // immutable int mWakeReadPipeFd; // immutable int mWakeWritePipeFd; // immutable Mutex mLock; Vector 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 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 mResponses; size_t mResponseIndex; nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
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_castandroid_os_MessageQueue.cpp(ptr); return nativeMessageQueue->wake(); }
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
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, jint ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_castandroid_os_MessageQueue.cpp(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis); // 調用NativeMessageQueue::pollOnce() }
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 spLooper.cpphandler = 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; }
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方法的實現都非常簡單,先看nativeIdling:
static jboolean android_os_MessageQueue_nativeIsIdling(JNIEnv* env, jclass clazz, jint ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_castandroid_os_MessageQueue.cpp(ptr); return nativeMessageQueue->getLooper()->isIdling(); }
NativeMessageQueue::getLooper:
inline spandroid_os_MessageQueue.cppgetLooper() const { return mLooper; }
bool Looper::isIdling() const { return mIdling; }Looper.cpp
再看nativeDestroy:
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_castandroid_os_MessageQueue.cpp(ptr); nativeMessageQueue->decStrong(env); }
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還是很有必要的。
基礎配置部分解說過程是從你Native加入rn模塊,新建的rn項目配置的東西都有首先 在你的項目根目錄同級開一個rn目錄(這裡只是舉個例子)mkdir xxx-rn &a
根據EditText搜索框ListView動態顯示數據是根據需求來的,覺得這之中涉及的東西可能比較的有意思,所以動手來寫一寫,希望對大家有點幫助。首先,我們來分析下整個過
最近在做一個直播的android手機app,難點在於流媒體的處理,主要是對流媒體進行編碼與傳輸,在此用H264編碼,傳輸協議采用RTMP,流媒體服務器用nginx並進行配
UISearchBar功能與UITextField類似,也是單行字符輸入框常用用途:用於搜索功能的實現使用注意事項:1、書簽按鈕屬性與搜索回車按鈕屬性不能同時進行設置,只