Handler機制,子線程爲什麼不能更新UI?
Android UI操作並不是線程安全的,並且這些操作必須在UI線程執行。
利用handler可以從子線程發送消息到主線程達到更新UI的目的;在子線程裏可以更新在子線程中加載的view(需要looper);在主線程的onCreate()中創建的子線程也可以更新主線程的UI,前提是不做其他耗時操作;除外,在onCreate()的子線程做耗時後更新UI報錯;子線程沒有looper想更新自己的UI也報錯;點擊事件(可以看做耗時事件)中的子線程更新主線程UI報錯。由此可以看出,持有view的線程都可以更改自己的view,主線程默認looper不需要手動添加。一般的更新其他線程的UI需要handler即線程間的通信但handler只是線程間傳遞數據,更新操作還是要rootview來完成。那爲什麼在onCreate()的子線程更新主線程UI沒有報錯呢?而稍一耗時就報錯了呢?必然是因爲更新UI快於異常線程檢測以至UI更新已經完了可能ViewRootImpl纔剛剛初始化完成,但這樣是不安全的,大家都不推薦這種方式。
而Handler更新主線程的UI也是在主線程中進行的,只不過通過handler對象將子線程等耗時操作中得到的數據利用message傳到了主線程。關於handler的原理,老生常談。溫故而知新。今天試着解釋一下相關的源碼,6.0以上的。
Looper是final修飾,不可繼承。
Class used to run a message loop for a thread.
Threads by default do not have a message loop associated with them; to create one ,call #prepare in the thread that is to run the loop,and then #loop to have it process messages until the loop is stopped.
Most interaction with a message loop is through the #Handler class.
Looper類的解釋告訴我們兩個主要方法,prepare和loop。主線程不需要顯示調用Looper的兩個方法。但在子線程中,則需要顯式調用。幾個全局變量:
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
ThreadLocal在這裏理解爲將Looper對象與當前線程綁定,在同一個線程作用域內可見,是一個Java工具類。一個靜態的looper引用,一個messageQueue引用,一個線程引用。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
prepare方法中傳入的布爾值最終傳給了MessageQueue的構造方法中,它代表了 True if the message queue can be quit。prepare方法1.得到了looper對象並且2.looper在實例化的時候同時獲取到當前線程的引用,還會3.實例化一個成員變量MessageQueue。
Looper中的構造方法是私有的:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的loop方法,首先會拿到looper和消息隊列實例,接着在無限循環中調用queue.next()取出隊列的消息,交給msg.target.dispatchMessage(msg)處理。這其中消息的發送正是由handler.sendMessageAtTime()來做。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
在第6行,調用myLooper方法返回了ThreadLocal保存的looper對象:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
第十行將looper實例化時的messageQueue傳給了新的引用MessageQueue,十七到二十行是無限循環,queue.next()取出消息。msg.target.dispatchMessage(msg)處理消息,msg.recycleUnchecked()回收資源。到此,消息隊列和輪詢已經建立,下面應該是發送消息了。那就來看一下Handler的代碼:
/**
* A Handler allows you to send and process {@link Message} and Runnable Handler對象允許發送處理和一個線程的消息隊列相關聯的message和runnable對象。
* objects associated with a thread's {@link MessageQueue}. Each Handler 每一個Handler實例都與一個單獨的線程和它的消息隊列關聯。
* instance is associated with a single thread and that thread's message 當創建一個Handler對象時,它就與創建它的線程和線程的消息隊列綁定了。
* queue. When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it -- from that point on, 至此,它就會分發消息和runnable對象到綁定的消息隊列,並且在它們從消息隊列取出時執行。
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
*
* <p>There are two main uses for a Handler: (1) to schedule messages and handler主要有兩個用處:一是安排messages和runnable對象在未來的某一刻執行;
* runnables to be executed as some point in the future; and (2) to enqueue 二是爲在其他線程執行的動作排隊(此處翻譯不當)
* an action to be performed on a different thread than your own.
*
* <p>Scheduling messages is accomplished with the 藉助post和send系列方法,調度消息得到完成。
* {@link #post}, {@link #postAtTime(Runnable, long)},
* {@link #postDelayed}, {@link #sendEmptyMessage},
* {@link #sendMessage}, {@link #sendMessageAtTime}, and
* {@link #sendMessageDelayed} methods. The <em>post</em> versions allow post允許當Runnable對象被接收且將要被messagequeue調用時爲它們排隊;
* you to enqueue Runnable objects to be called by the message queue when sendMessage允許爲一個包含了數據集且將會被handler的handleMessage方法(需要自己重寫)處理的消息對象排隊。
* they are received; the <em>sendMessage</em> versions allow you to enqueue
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler's {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
*
* <p>When posting or sending to a Handler, you can either 當用post或者send向handler發消息時,可以在消息隊列就緒時立即處理也可以指定延遲做延時處理。後者需要實現超時等時間行爲。
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
*
* <p>When a
* process is created for your application, its main thread is dedicated to 當應用中的進程創建時,主線程致力於運行消息隊列。隊列着重於頂層的應用組件如活動,廣播接收者等和任何這些組件創建的window。
* running a message queue that takes care of managing the top-level 可以創建子線程並且通過handler與主線程通信。和以前一樣,是靠調用post或者sendMessage來實現,當然,是在子線程中調用。
* application objects (activities, broadcast receivers, etc) and any windows 發出的Runnable對象或者消息就會調度到handler的消息隊列中並在恰當時處理。
* they create. You can create your own threads, and communicate back with
* the main application thread through a Handler. This is done by calling
* the same <em>post</em> or <em>sendMessage</em> methods as before, but from
* your new thread. The given Runnable or Message will then be scheduled
* in the Handler's message queue and processed when appropriate.
*/
說來慚愧,這一段類註釋翻譯花了好長時間,好歹六級也過了好多年了。從類的註釋中得知,handler主要用send和post發送消息,在重寫的handleMessage方法處理消息。
所有的send方法底層都是通過sendMessageAtTime實現的,其中在sendMessageDelayed方法中調用sendMessageAtTime時傳入了SystemClock.uptimeMills():
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
handler的enqueueMessage方法中將handler對象賦給了msg的target屬性,接着調用了MessageQueue的enqueueMessage方法。MessageQueue也是final類,算是這幾個類中比較native的,很多都是與底層交互的方法。在它的enqueueMessage方法中將message壓入消息隊列,接着loop()方法中msg.target.dispatchMessage(msg),上文已經提到了。Message也是final類。所以最後是調用handler的dispatchMessage方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
所以看到消息最後的處理正是在我們實例化handler時覆寫的HandlerMessage方法,至此,消息發送處理機制走完了。
那麼總結一下:消息機制的過程是,Looper.prepare()實例化looper對象和消息隊列,handler實例化獲得上一步的looper對象和消息隊列的引用,handler.sendMessageAtTime()發送消息到消息隊列(這其中包括了給message的target賦值,將message壓入到消息隊列),Looper.loop()輪詢隊列取出消息交給message.target.dispatchMessage()處理,實質上是調用了我們自己重寫的handleMessage()。而Android爲我們做了大量的封裝工作。開發人員只需要構造message併發送,自定義消息處理邏輯就可以了。
在研究源碼時,首先看類註釋,接着明確自己的需求,再去找關鍵方法,千萬莫要在龐雜的代碼中迷失。
在探尋源碼的過程中,發現了下一次博客的內容,就是WindowManager.LayoutParams,SystemClock,ThreadLocal,AtomicInteger。
水往低處流,人往高處走。