1. 程式人生 > >Android訊息機制Handler原理分析

Android訊息機制Handler原理分析

文章目錄

1、App中Handler的使用

  我們都知道,Android的UI執行緒是執行緒不安全的,如果在子執行緒中嘗試進行UI操作,程式就有可能會崩潰。相信大家在日常的工作當中都會經常遇到這個問題,解決的方案應該也是早已爛熟於心,即建立一個Message物件,然後藉助Handler傳送出去,之後在Handler的handleMessage()方法中獲得剛才傳送的Message物件,然後在這裡進行UI操作就不會再出現崩潰了。具體實現程式碼如下:

public static final int WHAT = 1;

//宣告一個主執行緒的Handler
private Handler handler = new Handler(
) { public void handleMessage(Message msg) { switch (msg.what) { case WHAT: //TODO break; default: break; } } }; //建立一個執行緒並向主執行緒傳送Message new Thread(new Runnable() { @Override public void run() { Message msg = handler.
obtainMessage(); message.what = WHAT; handler.sendMessage(message); } }).start();

那麼Handler的原理是什麼呢?

2、Java層Handler的原理

2.1 Handler模型

Handler機制是由Looper和MessageQueue來構建訊息機制的。訊息機制主要包含:

Message: 執行緒間通訊的資料單元,可以封裝資料 。訊息分為硬體產生的訊息(如按鈕、觸控)和軟體生成的訊息;
MessageQueue: 訊息佇列用來存放通過Handler傳送的訊息,它是一個按Message的when排序的優先順序佇列。(when:代表著被處理的時間)
Handler: 訊息輔助類,主要功能向訊息池傳送各種訊息事件(Handler.sendMessage())和處理相應訊息事件(Handler.handleMessage());
Looper: 不斷迴圈執行(Looper.loop()),按分發機制將訊息分發給目標處理者。

2.2 圖解Handler

我們先通過一張圖整體瞭解一下Handler的處理流程,之後在詳細分析原始碼。
在這裡插入圖片描述

圖解:

  1. 首先在使用Handler之前需要呼叫Looper.prepare()建立Looper物件,在該方法中會建立MessageQueue物件,該物件以連結串列的形式儲存Message;
  2. 之後呼叫Looper.loop()方法,在該方法中會呼叫MessageQueue.next()方法獲取Message,但是由於此時MessageQueue為空,Looper會阻塞等待訊息;
  3. 當有訊息傳送時會呼叫Handler.sendMessage()方法,該方法會呼叫MessageQueue.enqueueMessage()方法將Message插入到MessageQueue中;
  4. 由於此時MessageQueue中存在訊息,Looper會被喚醒並從MessageQueue中獲取Message,之後會呼叫Handler.dispatchMessage()方法;
  5. Handler.dispatchMessage()會尋找target物件並呼叫其handleMessage方法;

2.3 Handler執行緒的典型例項

先展示一個典型的關於Handler/Looper的執行緒:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
    	// Looper.prepare()在每個執行緒只允許執行一次,該方法會建立Looper物件,Looper的構造方法中會建立一個MessageQueue物件
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //TODO
            }
        };
        Looper.loop();
    }
}

2.4 Looper

2.4.1. Looper.prepare()

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
    	// 如果此執行緒之前呼叫過Looper.prepare()方法會丟擲異常
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

/**
 1. Initialize the current thread as a looper, marking it as an
 2. application's main looper. The main looper for your application
 3. is created by the Android environment, so you should never need
 4. to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    // 呼叫Looper.prepare(false);
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

Looper.prepare()會建立Lopper物件並講自己加入到ThreadLocal中

2.4.2. Looper建構函式

private Looper(boolean quitAllowed) {
    // 建立MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper的構造方法會建立MessageQueue
2.4.3. Looper.loop()

/**
 1. Run the message queue in this thread. Be sure to call
 2. {@link #quit()} to end the loop.
*/
public static void loop() {
   final Looper me = myLooper();
   if (me == null) {
       // 如果此執行緒在呼叫Looper.loop()之前未呼叫Looper.prepare()則丟擲異常
       throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
   }
   // 獲取Looper物件中的MessageQueue
   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();

   // Allow overriding a threshold with a system prop. e.g.
   // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
   final int thresholdOverride =
           SystemProperties.getInt("log.looper."
                   + Process.myUid() + "."
                   + Thread.currentThread().getName()
                   + ".slow", 0);

   boolean slowDeliveryDetected = false;

   for (;;) {
       // 呼叫MessageQueue的next()方法,如果MessageQueue中有Message則返回,如果MessageQueue中沒有Message則此處會阻塞等待訊息
       Message msg = queue.next(); 
       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);
       }

           .
           .  // 此處程式碼省略
           .
       
       try {
       	   // 用於分發Message
           msg.target.dispatchMessage(msg);
           dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
       } finally {
           if (traceTag != 0) {
               Trace.traceEnd(traceTag);
           }
       }
       
           .
           .  // 此處程式碼省略
           .

       msg.recycleUnchecked();
   }
}

loop()進入迴圈模式,不斷重複下面的操作:

  1. 讀取MessageQueue的下一條Message;
  2. 把Message分發給相應的target;
  3. 再把分發後的Message回收到訊息池,以便重複利用。

2.5 Handler

2.5.1. Handler建構函式

/**
 * 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 respect 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());
        }
    }
    // 必須先執行Looper.prepare(),才能獲取Looper物件,否則為null.
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        // 如果未執行Looper.prepare()則丟擲異常
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

那為什麼我們在APP中使用Handler的時候沒有呼叫Looper.prepare()但是程式仍然能夠正常執行呢?
這是因為Android在啟動App的時候Zygote會建立App程序,之後會呼叫該程序ActivityThread的main方法

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // 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());

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可見在ActivityThread的main方法中系統已經為我們建立好了Looper,所以在我們使用Handler的時候並不需要呼叫Looper.prepare()方法

接下來我們繼續看Handler中的其他方法
2.5.2. Handler.dispatchMessage()

/**
 1. Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當Message存在回撥方法,回撥msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //當Handler存在Callback成員變數時,回撥方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

分發訊息流程:

  1. 當Message的回撥方法不為空時,則回撥方法msg.callback.run(),其中callBack資料型別為Runnable,否則進入步驟2;
  2. 當Handler的mCallback成員變數不為空時,則回撥方法mCallback.handleMessage(msg),否則進入步驟3;
  3. 呼叫Handler自身的回撥方法handleMessage(),該方法預設為空,Handler子類通過覆寫該方法來完成具體的邏輯。

2.5.3. Handler.sendMessage()

/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received 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.
 */
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

/**
 * 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.sendMessage()等系列方法最終呼叫MessageQueue.enqueueMessage(msg, uptimeMillis),將訊息新增到MessageQueue中。

2.6 MessageQueue

MessageQueue是Handler的Java層和C++層的連線紐帶,大部分核心方法都交給native層來處理,其中MessageQueue類中涉及的native方法如下:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
關於這些native方法會在第三節介紹

2.6.1. MessageQueue建構函式

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //通過native方法初始化MessageQueue,其中mPtr儲存的是NativeMessageQueue的引用
    mPtr = nativeInit();
}

2.6.2. MessageQueue.next()

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        // 當訊息迴圈已經退出,則直接返回
        return null;
    }
    
    // 迴圈迭代的首次為-1
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 阻塞操作,等待nextPollTimeoutMillis時長,或者MessageQueue被喚醒
        nativePollOnce(ptr, 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) {
                // 當訊息Handler為空時,查詢MessageQueue中的下一條非同步訊息Message,則退出迴圈。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 當非同步訊息觸發時間大於當前時間,則設定下一次輪詢的超時時長
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 取出一條訊息,並返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    // 設定訊息的使用狀態,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    //成功地獲取MessageQueue中的下一條即將要執行的Message
                    return msg;
                }
            } else {
                // 沒有訊息
                nextPollTimeoutMillis = -1;
            }
        
             .
             .  // 此處程式碼省略
             .
             
    }
}

nativePollOnce是阻塞操作(基於epoll機制),其中nextPollTimeoutMillis代表下一個訊息到來前,還需要等待的時長;當nextPollTimeoutMillis = -1時,表示MessageQueue中無訊息,會一直等待下去。

當處於空閒時,往往會執行IdleHandler中的方法。當nativePollOnce()返回後,next()從mMessages中提取一個訊息。

nativePollOnce()在native做了大量的工作,想進一步瞭解可檢視Native層Handler的原理。

2.6.3. MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    // 每一個Message必須有一個target
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()