Android訊息處理機制——Looper、Handler、Message 原始碼分析
阿新 • • 發佈:2019-01-11
原文地址:http://blog.csdn.net/wzy_1988/article/details/38346637
前言
雖然一直在做應用層開發,但是我們組是核心系統BSP,瞭解底層瞭解Android的執行機制還是很有必要的。就應用程式而言,Android系統中的Java應用程式和其他系統上相同,都是靠訊息驅動來工作的,它們大致的工作原理如下:- 有一個訊息佇列,可以往這個訊息佇列中投遞訊息。
- 有一個訊息迴圈,不斷從訊息佇列中取出訊息,然後處理 。
Looper類分析
- publicfinalclass Looper {
- privatestaticfinal String TAG = "Looper";
- // sThreadLocal.get() will return null unless you've called prepare().
- staticfinal ThreadLocal<Looper> sThreadLocal =
- privatestatic Looper sMainLooper; // guarded by Looper.class
- final MessageQueue mQueue;
- final Thread mThread;
- private Printer mLogging;
- /** 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()}.
- */
- publicstaticvoid prepare() {
- prepare(true);
- }
- privatestaticvoid prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- thrownew RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
- /**
- * Initialize the current thread as a looper, marking it as an
- * application's main looper. The main looper for your application
- * is created by the Android environment, so you should never need
- * to call this function yourself. See also: {@link #prepare()}
- */
- publicstaticvoid prepareMainLooper() {
- prepare(false);
- synchronized (Looper.class) {
- if (sMainLooper != null) {
- thrownew IllegalStateException("The main Looper has already been prepared.");
- }
- sMainLooper = myLooper();
- }
- }
- /** Returns the application's main looper, which lives in the main thread of the application.
- */
- publicstatic Looper getMainLooper() {
- synchronized (Looper.class) {
- return sMainLooper;
- }
- }
- /**
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- publicstaticvoid loop() {
- final Looper me = myLooper();
- if (me == null) {
- thrownew 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();
- finallong 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);
- }
- msg.target.dispatchMessage(msg);
- 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.
- finallong 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();
- }
- }
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- publicstatic Looper myLooper() {
- return sThreadLocal.get();
- }
- publicvoid setMessageLogging(Printer printer) {
- mLogging = printer;
- }
- /**
- * Return the {@link MessageQueue} object associated with the current
- * thread. This must be called from a thread running a Looper, or a
- * NullPointerException will be thrown.
- */
- publicstatic MessageQueue myQueue() {
- return myLooper().mQueue;
- }
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mThread = Thread.currentThread();
- }
- /**
- * Returns true if the current thread is this looper's thread.
- * @hide
- */
- publicboolean isCurrentThread() {
- return Thread.currentThread() == mThread;
- }
- publicvoid quit() {
- mQueue.quit(false);
- }
- publicvoid quitSafely() {
- mQueue.quit(true);
- }
- publicint postSyncBarrier() {
- return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
- }
- publicvoid removeSyncBarrier(int token) {
- mQueue.removeSyncBarrier(token);
- }
- /**
- * Return the Thread associated with this Looper.
- */
- public Thread getThread() {
- return mThread;
- }
- /** @hide */
- public MessageQueue getQueue() {
- return mQueue;
- }
- /**
- * Return whether this looper's thread is currently idle, waiting for new work
- * to do. This is intrinsically racy, since its state can change before you get
- * the result back.
- * @hide
- */
- publicboolean isIdling() {
- return mQueue.isIdling();
- }
- publicvoid dump(Printer pw, String prefix) {
- pw.println(prefix + toString());
- mQueue.dump(pw, prefix + " ");
- }
- public String toString() {
- return"Looper (" + mThread.getName() + ", tid " + mThread.getId()
- + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
- }
- }
- package com.example.testlibrary;
- import android.os.Handler;
- import android.os.Looper;
- publicclass LooperTheread extends Thread{
- public Handler mhHandler;
- @Override
- publicvoid run() {
- // 1. 呼叫Looper
- Looper.prepare();
- // ... 其他處理,例如例項化handler
- // 2. 進入訊息迴圈
- Looper.loop();
- }
- }
Looper.prepare()
在呼叫prepare的執行緒中,new了一個Looper物件,並將這個Looper物件儲存在這個呼叫執行緒的ThreadLocal中。而Looper物件內部封裝了一個訊息佇列。我們來看一下Looper類的原始碼。第一個呼叫函式是Looper的prepare函式,它的原始碼如下: [java] view plain copy print?
- // 每個執行緒中的Looper物件其實是一個ThreadLocal,即執行緒本地儲存(TLS)物件
- staticfinal ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- publicstaticvoid prepare() {
- prepare(true);
- }
- privatestaticvoid prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- thrownew RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mThread = Thread.currentThread();
- }
Looper迴圈
呼叫了Loop方法後,Looper執行緒就開始真正的工作了,它不斷從自己的MessageQueue中取出對頭的資訊(也叫任務)執行,如圖所示:其實現原始碼如下所示(這裡我做了一些修整,去掉不影響主線的程式碼): [java] view plain copy print?
- /**
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- publicstaticvoid loop() {
- final Looper me = myLooper();
- if (me == null) {
- thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- // 取出這個Looper的訊息佇列
- final MessageQueue queue = me.mQueue;
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // 處理訊息,Message物件中有一個target,它是Handler型別
- msg.target.dispatchMessage(msg);
- msg.recycle();
- }
- }
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- publicstatic Looper myLooper() {
- return sThreadLocal.get();
- }
- 封裝了一個訊息佇列。
- Looper的prepare函式把這個Looper和呼叫prepare的執行緒(也就是最終處理的執行緒)繫結在一起,通過ThreadLocal機制實現的。
- 處理執行緒呼叫loop函式,處理來自該訊息佇列的訊息。
Handler分析
什麼是handler?handler扮演了往MessageQueue裡新增訊息和處理訊息的角色(只處理由自己發出的訊息),即通過MessageQueue它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是非同步的。初識Handler
Handler中的所包括的成員變數: [java] view plain copy print?- final MessageQueue mQueue; // Handler中也有一個訊息佇列
- final Looper mLooper; // 也有一個Looper
- final Callback mCallback; // 有一個回撥類
- public Handler() {
- this(null, false);
- }
- /**
- * Use the {@link Looper} for the current thread with the specified callback interface
- * and set whether the handler should be asynchronous.
- *
- * Handlers are synchronous by default unless this constructor is used to make
- * one that is strictly asynchronous.
- *
- * Asynchronous messages represent interrupts or events that do not require global ordering
- * with represent to synchronous messages. Asynchronous messages are not subject to
- * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
- *
- * @param callback The callback interface in which to handle messages, or null.
- * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
- * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
- *
- * @hide
- */
- public Handler(Callback callback, boolean async) {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- thrownew RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- publicstatic Looper myLooper() {
- return sThreadLocal.get();
- }
Handler真面目
由上面分析可知,Handler中的訊息佇列實際上就是Handler所屬執行緒的Looper物件的訊息佇列,我們可以為之前的LooperThread類增加Handler,程式碼如下: [java] view plain copy print?- publicclass LooperThread extends Thread{
- public Handler mhHandler;
- @Override
- publicvoid run() {
- // 1. 呼叫Looper
- Looper.prepare();
- // ... 其他處理,例如例項化handler
- Handler handler = new Handler();
- // 2. 進入訊息迴圈
- Looper.loop();
- }
- }
- 呼叫Looper的myQueue,它將返回訊息佇列物件MessageQueue。
- 構造一個Message,填充它的成員,尤其是target物件。
- 呼叫MessageQueue的enqueueMessage,將訊息插入到訊息佇列中。
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
- publicfinalboolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- privatestatic Message getPostMessage(Runnable r) {
- Message m = Message.obtain(); // 得到空的message
- m.callback = r; // 將runnable設定為message的callback
- return m;
- }
- publicfinalboolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
- publicboolean 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);
- returnfalse;
- }
- return enqueueMessage(queue, msg, uptimeMillis);
- }
- privateboolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- msg.target = this; // 將Message的target設定為當前的Handler,然後將訊息自己加到訊息佇列中
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
Handler處理訊息
講完了訊息傳送,再看一下Handler是如何處理訊息的。訊息的處理是通過核心方法dispatchMessage(Message msg)與鉤子方法handleMessage(Message msg)完成的,原始碼如下: [java] view plain copy print?- /**
- * Handle system messages here.
- */
- publicvoid dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
- privatestaticvoid handleCallback(Message message) {
- message.callback.run();
- }
- /**
- * Subclasses must implement this to receive messages.
- */
- publicvoid handleMessage(Message msg) {
- }
- 如果Message自帶了callback處理,則交給callback處理。例如上文分析的,Handler裡通過post(Runnable r)發生一個Runnable物件,則msg的callback物件就被賦值為Runnable物件。
- 如果Handler設定了全域性的mCallback,則交給mCallback處理。
- 如果上述都沒有,該訊息會被交給Handler子類實現的handlerMessage(Message msg)來處理。當然,這需要從Handler派生並重寫HandlerMessage函式。
Handler的用處
看完了Handler的傳送訊息和處理訊息,我們來學習一下Handler被稱為非同步處理大師的真正牛逼之處。Hanlder有兩個重要的特點: 1. handler可以在任意執行緒上傳送訊息,這些訊息會被新增到Handler所屬執行緒的Looper物件的訊息佇列裡。2. handler是在例項化它的執行緒中處理訊息的。
這解決了Android經典的不能在非主執行緒中更新UI的問題。Android的主執行緒也是一個Looper執行緒,我們在其中建立的Handler將預設關聯主執行緒Looper的訊息佇列。因此,我們可以在主執行緒建立Handler物件,在耗時的子執行緒獲取UI資訊後,通過主執行緒的Handler物件引用來發生訊息給主執行緒,通知修改UI,當然訊息了還可以包含具體的UI資料。
Message
在整個訊息處理機制中,Message又叫做task,封裝了任務攜帶的訊息和處理該任務的handler。Message的原始碼比較簡單,原始碼位置(frameworks/base/core/java/android/os/Message.java)這裡簡單說明幾點注意事項: 1. 儘管Message有public的預設構造方法,但是你應該通過Message.obtain()來從訊息池中獲得空訊息物件,以節省資源,原始碼如下: [java] view plain copy print?- /**
- * Return a new Message instance from the global pool. Allows us to
- * avoid allocating new objects in many cases.
- */
- publicstatic Message obtain() {
- synchronized (sPoolSync) {
- if (sPool != null) {
- Message m = sPool;
- sPool = m.next;
- m.next = null;
- sPoolSize--;
- return m;
- }
- }