Android開發系列11——Handler消息處理機制

前言

  Android的消息處理機制其實就是另外一種形式的”事件處理“,這種機制主要是爲了解決Android應用的多線程問題。

理解Handler的消息機制,首先要從幾個方面瞭解Handler消息處理機制。

  • Handler是什麼?(Handler的概念)
  • Handler可以做什麼?(Handler的用途)
  • Handler怎麼使用?(Handler的用法)

其次,需要了解Handler消息處理過程中用到的類,這些類具體是什麼怎麼用等。

一、Handler

Handler具體的解釋和方法可以參考:Handler.java類文件。

1.Handler是什麼?(Handler的概念)

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread’s message 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, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

簡單翻譯:

Handler是一個可以通過關聯一個消息隊列來發送和處理消息,發送或處理Runnable對象的一個處理程序,每個Handler都關聯單個的線程和消息隊列,當你創建一個新的Handler,Handler將綁定到線程消息隊列(假設:A)上,從正在創建Handler對象時間點所在的線程就是綁定的線程消息隊列(在那個線程創建,就在那個線程綁定消息隊列),這個Handler爲消息隊列(假設:A)提供消息和Runnable對象,執行從消息隊列釋放出來的消息和Runnable對象。

2.Handler可以做什麼?(Handler的用途)

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

簡單翻譯:

Handler有兩個主要的用途:

  • 1.在將來的摸一個時間點執行 消息和 Runnable
  • 2.將一個動作(Action)放在不同於所在的線程上執行

3.Handler怎麼使用?(Handler的用法)

Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).


When posting or sending to a Handler, you can either 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.


When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows 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 post or sendMessage 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.

簡單翻譯:

通過重寫post(Runnable)、postAtTime(Runnable, long)、postDelayed(Runnable, long)、sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message, long)、sendMessageDelayed(Message, long)來完成發送消息。當前版本允許你通過消息隊列接受一個Runnable對象,sendMessage方法當前版本允許你將一個包的數據通過消息隊列的方式處理,但是你需要重寫Handler的handleMessage方法


當發佈<處理>和發送一個Handler時,你既可以在消息隊列準備就緒立即處理,或者指明一個延遲處理時間或者絕對的時間(系統具體時間)去執行,後邊這兩個都允許你完成,在超時、ticks(系統的相對時間單位)和其他時間段爲基礎的行爲


即當一個進程被應用程序創建時,它的主線程會運行一個消息隊列負責管理它創建的高層應用程序對象(如Activity、Broadcast Receiver等)和任何它的窗口創建的對象,你可以通過一個Handler,創建自己的線程來實現與主線程之間的交互,但前提是你得在你的線程重寫sendMessage方法並寫上一行行代碼,這樣你給定的Runnable或者Message將被MessageQueue(消息隊列)預定,並在合適的時間處理

二、Handler相關的類

1.Looper

Looper的Class文件中,查看Looper的詳細解釋

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 {@link #prepare} in the thread that is to run the loop, and then{@link #loop} to have it process messages until the loop is stopped.


Most interaction with a message loop is through the{@link Handler} class.


This is a typical example of the implementation of a Looper thread,using the separation of {@link #prepare} and {@link #loop} to create an initial Handler to communicate with the Looper.

簡單的翻譯:

Looper被用於線程運行的消息循環的類,默認線程並沒有和消息循序之間相互關聯,所以創建一個Looper,在Thread中Looper調用prepare運行消息循環,讓後調用loop()讓它循環處理消息,知道循環停止。
很多和消息循環的交互都是通過Handler類
這是一個典型Looper線程的實例,它用prepare()和loop()的分割來創建一個初始化的Handler與Looper進行通信。

class LooperThread extends Thread {
     public Handler mHandler;

     public void run() {
         Looper.prepare();

         mHandler = new Handler() {
             public void handleMessage(Message msg) {
                 // process incoming messages here
             }
         };
         Looper.loop();
     }
 }

2.Message

Message的Class文件中,查看Message的詳細解釋

Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.


While the constructor of Message is public, the best way to get one of these is to call {@link #obtain Message.obtain()} or one of the {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull them from a pool of recycled objects.

簡單翻譯:

定義一條包含描述和任意數據對象的消息,該對象可以是發送到{@link Handler}。 該對象包含兩個額外的int字段和一個額外的對象字段,使您在許多情況下不進行分配。


雖然Message的構造函數是公共的,但最好的獲取方法其中之一是調用{@link #obtainMessage.obtain()}或其中之一{@link Handler#obtainMessage Handler.obtainMessage()}方法,該方法將 它們來自回收對象池。

3.MessageQueue

Low-level class holding the list of messages to be dispatched by a {@link Looper}. Messages are not added directly to a MessageQueue, but rather through {@link Handler} objects associated with the Looper.


You can retrieve the MessageQueue for the current thread with {@link Looper#myQueue() Looper.myQueue()}.

簡單的翻譯:

包含{@link Looper}要分派的消息列表的低級類。 消息不是直接添加到MessageQueue,而是通過與Looper關聯的{@link Handler}對象添加。


您可以使用{@link Looper#myQueue()Looper.myQueue()}檢索當前線程的MessageQueue。

三、Handler運行流程

通過上述的概念描述了Handler運行過程中的所有Class,接下來會詳細講解一下Handler的的運行的流程,以及相關的Class在Handler運行過程中的作用。

  • Message: Handler接收 和 處理的消息對象。
  • Looper: 每個線程只能擁有一個Looper,它的loop方法負責讀取MessageQueue中的消息,讀到消息之後就把消息交給發送消息的Handler進行處理。
  • MessageQueue: 消息隊列,採用先進先出的方式管理Message。程序在創建Looper對象時,會在它的構造器中創建MessageQueue對象。
  • Handler: 它的作用主要有兩個——發送消息和處理消息。

由於Handler的作用是:發送消息和處理消息,發送Message消息,必須有一個MessageQueue進行接收,而MessageQueue必須由Looper負責管理。如下圖:

1.發送Message消息
2.Message必須被Message保存
3.MessageQueue需要Looper來管理
Handler創建需要關聯一個Looper
Handler
Message
Looper
MessageQueue

所以Handler正常工作之前,必須在當前線程中必須一個Looper對象。(默認線程內比沒有Looper,但在UI主線中,一直存在一個Looper對象運行

1.Handler類底層分析運行流程

Handler類內的方法可以分爲三類:(如下圖)

  • 構造Handler函數
  • 發送Message消息
  • 接收Message消息
  • 從MessageQueue中獲取Message消息

Handler類中的屬性對象:

 @UnsupportedAppUsage
    final Looper mLooper;        // Looper 對象
    final MessageQueue mQueue;   // 消息管理類
    @UnsupportedAppUsage
    final Callback mCallback;     // CallBack 回調
    final boolean mAsynchronous;    //  異步標識
    @UnsupportedAppUsage
    IMessenger mMessenger;      //消息

Handler類方法Handler類方法

1)構造Handler方法

無參數的Handler構造方法(默認讀取當前線程中的Looper,如果不存在會拋出異常

/**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * 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構造方法:

  • 從構造方法上的解釋可知,Handler上的mQueue是Looper對象內的。(和上邊講述的一致)
  • callback 這個callback接口實現handle消息。
/**
     * 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(@Nullable 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) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

/**
     * Use the provided {@link Looper} instead of the default one and take a callback
     * interface in which to handle messages.  Also 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 conditions such as display vsync.
     *
     * @param looper The looper, must not be null.
     * @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
     */
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

2) Handler中的callback

從Handler類方法中,找到callback對應的方法,從官方解釋:Handler的子類實現Callback接口(handleMessage)才能處理Handler接收到的消息。

/**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }
    
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

總之,Handler在初始化之前,Handler初始化所在線程必須有Looper對象(只有UI主線程纔有Looper),所以在子線程中使用Handler,需要創建Looper對象。

四、Looper的運行

Looper類的方法如下:
Looper類方法
Looper的構造方法是一個私有的方法,在用戶層面無法調用這個構造方法來構造Looper的對象。

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

Looper有一個經典在線程中的例子,用於創建一個新的Looper對象。(由於除UI主線程之外的線程,都不會創建和關聯Looper對象,必須創建一個Looper對象)

class LooperThread extends Thread {
     public Handler mHandler;

     public void run() {
         Looper.prepare();

         mHandler = new Handler() {
             public void handleMessage(Message msg) {
                 // process incoming messages here
             }
         };
         Looper.loop();
     }
 }

所以Looper在線程中,Handler進行傳值需要進行兩步操作:

  • Looper.prepare();
  • Looper.loop();

Looper.prepare()

/** 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)); // 創建一個新的Looper對象
    }

	// 創建Looper對象
	private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();   // 當前所在的線程
  }

Looper在調用Looper.prepare(), 創建一個新的Looper對象( sThreadLocal.set(new Looper(quitAllowed));)。並且創建Handler,把Handler和當前線程中的Looper關聯起來。

Looper.loop();

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

        // 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 (;;) {
            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);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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.
            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();
        }
    }


/**
     * 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.loop()的源碼,可以看出loop對消息隊列進行運行,顯現Handler的消息傳遞。

總結:

  Handler主要是用於線程之間的異步通信管理的抽象類,線程之間傳遞的的Message需要通過Looper的Message隊列管理,Looper是Handler實現線程之間消息傳遞比不缺少的一個循環消息管理對象。

  主線程中存在Looper對象。所以不需要創建,只需要進行發送消息和接收消息即可。

  其他線程中Looper對象需要通過prepare()來創建Looper,通過loop()來運行Looper。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章