Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從Handler+Message+Looper源碼帶你分析Android系統的消息處理機制

從Handler+Message+Looper源碼帶你分析Android系統的消息處理機制

編輯:關於Android編程

引言

【轉載請注明出處:從Handler+Message+Looper源碼帶你分析Android系統的消息處理機制 CSDN 廢墟的樹】

作為Android開發者,相信很多人都使用過Android的Handler類來處理異步任務。那麼Handler類是怎麼構成一個異步任務處理機制的呢?這篇

博客帶你從源碼分析Android的消息循環處理機制,便於深入的理解。

這裡不得不從“一個Bug引發的思考”開始研究Android的消息循環處理機制。說來話長,在某一次的項目中,原本打算開啟一個工作線程

WorkThread去執行一個耗時任務,然後在工作線程WorkThread中new一個Handler對象來發送消息。代碼簡化成如下:

private class WorkThread extends Thread {
        private Handler mHandler;

        @Override
        public void run() {

            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 0:
                            Log.e(TAG, 任務執行完成);
                            break;
                    }
                }
            };

            //模擬一個耗時任務
            try {
                sleep(9000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //任務執行完成之後發送一個消息
            int what = 0;
            mHandler.sendEmptyMessage(what);
        }
    }

當以上代碼執行之後,很不幸的出現了如下的一個Bug:

這裡寫圖片描述

Log打印日志提示:不能在線程中沒有調用 Looper.prepare()方法之前就去創建handler對象,言外之意就是,在線程中還沒調Looper.prepare()

方法之前,你是不能去創建Handler對象的,否則拋出錯誤異常。為什麼會這樣呢?帶著疑問,我們跟蹤代碼進入到Handler源碼中的構造方法:

public Handler(Callback callback, boolean async) {
        .............

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                Can't create handler inside thread that has not called Looper.prepare());
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

代碼第5-8行:我們發現當成員變量mLooper為空值時,就會拋出上面的異常了,意思就是剛才在WorkThread中創建Handler的時候mLooper是

空的,難道不可以這樣創建Handler消息處理機制?那為什麼在UI線程中直接new一個Hanlder對象不會出錯呢?帶著這種好奇心,我們今天來分

析一下Android系統消息處理機制有關的 Handler,Message,Looper,Thread類之間的關聯。

Handler:消息的執行者,也可以稱之為異步任務的執行者 Message:消息的封裝者,把異步任務,消息碼Handler對象等封裝成Message對象 MessageQueue:消息隊列,用於保存當前線程的所有消息Message對象的一個列表 Looper:循環者,能讓工作線程變成循環線程,然後從消息隊列中循環讀取消息 Thread:異步任務或者耗時任務執行場所,一般開啟一個新的工作線程處理耗時任務

消息的執行者–Handler

在Android的消息處理機制中,Handler扮演者重要的角色。Handler負責如下幾個工作:

消息的發送 消息的入列 消息的調度/消息的分發 消息的處理

1-1消息的發送

對於消息的發送,相信很多人平時用的最多的是Handler.sendEmptyMessage(),那麼利用Handler發送的消息最終會發送到哪裡呢?騷年不用YY了,源碼會告訴你答案,我們跟蹤Handler類中的sendEmptyMessage方法:

/**
     * Sends a Message containing only the what value.
     *  
     * @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 sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
/**
     * Sends a Message containing only the what value, to be delivered
     * after the specified amount of time elapses.
     * @see #sendMessageDelayed(android.os.Message, long) 
     * 
     * @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 sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
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消息的發送最終都會調用sendMessageAtTime成員方法。該方法首先判斷當前Handler的成員變量mQueue是否為空,如果為空,則打印一個警告,並且返回false,表

示該消息發送失敗。那麼成員變量mQueue是神馬東西呢?mQueue是MessageQueue對象,而MessageQueue類是一個消息隊列,被Looper

對象持有。關於MessageQueue消息隊列相關內容後面會展開分析。繼續分析代碼,如果消息隊列不為空,則會調用enqueueMessage方法,跟蹤代碼進入該方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

首先,將當前類Handler對象賦值給消息Message類中的target成員變量。然後調用消息隊列MessageQueue類中的enqueueMessage方法將

該消息msg插入到消息隊列中。這個過程稱之為消息入列的一個過程。

1-2消息的入列

有1-1節可知,消息的入列是調用MessageQueue消息隊列類中的enqueueMessage成員方法實現的,跟蹤代碼看看消息入列的具體實現

 boolean enqueueMessage(Message msg, long when) {
        //判斷消息的目標對象是否為空
        if (msg.target == null) {
            throw new IllegalArgumentException(Message must have a target.);
        }
        //判斷消息是否正在使用
        if (msg.isInUse()) {
            throw new IllegalStateException(msg +  This message is already in use.);
        }

        synchronized (this) {
            //判斷是否正在清除消息隊列
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target +  sending message to a Handler on a dead thread);
                Log.w(MessageQueue, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            //標記消息正在使用
            msg.markInUse();
            //對消息的延時處理時間幅值
            msg.when = when;
            //保存消息隊列中的延時時間最小的那個消息
            Message p = mMessages;
            boolean needWake;
            //條件判斷,消息入列,將延時時間最小的那個消息賦值給mEssages變量
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                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
                // 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.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

分析:
1.代碼第3行:判斷當前入列的消息的目標處理對象是否為空,有1-1節我們知道msg.target的值是一個Handler實例。換言之,如果消息的目標處

理對象Handler為null,那麼該消息就沒有Handler去處理它,因此此處拋出一個異常

2.代碼第7行:判斷當前Message是否正在使用?如果是則拋出異常。說明一個相同的消息不可能同時存在同一個MessageQueue消息隊列中。

3.代碼第13行:判斷當前消息隊列是否正在退出?如果正在退出,則拋出異常,說明當前Message消息無法入列,因為MessageQueue消息隊列退出了。

4.代碼第27-52行:Message消息按照延時時間大小插入當前MessageQueue消息隊列中,最終延時時間最短的消息在隊列的最前面。

總結:到此,我們知道Handler將消息發送到消息隊列MessageQueue中去了。那麼這個消息隊列MessageQueue是從哪裡來

的呢?它屬於誰呢?這裡直接給出答案,MessageQueue消息隊列是在Looper類中創建,Looper循環則持有當前線程中的消息隊列

MessageQueue。至於為什麼是這樣,後面給出詳細解釋。

1-3消息的調度/消息的分發

有1-2小節我們知道,消息的執行者Handler將消息Message發送到消息隊列MessageQueue中,並且消息隊列中的所有消息都是按照時間排列。

那麼在消息隊列MessageQueue中的消息又是怎麼分發出去的,或者說消息隊列中的消息是怎麼被消費掉的?現在我們來解答1-2小節中的最後一個問題,消息隊列MessageQueue從哪裡來?屬於誰?在文章的一開頭,

我們有“一個Bug引發的思考”知道了在線程中創建Handler對象之前需要調用Looper.prepare(),否則會拋出異常。那麼我們就來分析一下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. * *


  *  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類作用是為一個線程構造消息循環的,因為線程Thread默認是不帶消息循環的。因此

你可以調用Looper類中的prepare方法去為構造一個帶消息循環的線程,並且調用Looper.loop()方法啟動循環去循環處理消息,直到loop循環結

束。並且Google官方還提供了一個標准的構造一個帶消息循環的線程實例,代碼如下:

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.prepare()方法做了什麼:

public final class Looper {
    private static final String TAG = Looper;

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal sThreadLocal = new ThreadLocal();
    private static 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()}.
      */
    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));
    }
}

分析:
1.代碼第24行:調用sThreadLocal.get()獲得存儲在本地線程中的Looper值,如果本地線程ThreadLocal中有當前的Looper對象,者會拋出一個異

常:”Only one Looper may be created per thread”每一個線程只能有一個Looper對象。所以每一個線程只能擁有一個Looper對象,關於ThreadLocal類,這裡不展開學習,主要作用是將Looper對象存儲在本地線程ThreadLocal中。

2.代碼第27行:將當前的Looper對象保存在本地線程對象ThreadLocal中。至此,Looper的准備工作就完成了。

Looper的准備工作完成之後,我們來看看Looper的啟動方法Looper.loop(),進入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();

        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.
            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();
        }
    }

分析:注釋開頭就描述了,該方法的作用是為了在線程中構建一個消息循環機制,當然你可以調用Looper類中的quit()成員方法結束當前loop循環。

代碼第10行:獲得當前線程Thread的消息隊列並且賦值給本地變量queue。

代碼第17-49行:構建一個死循環來遍歷當前Looper中的消息隊列。

代碼第18-22行:每次遍歷都獲取消息隊列中最前端的消息,也就是延時時間最短的消息。當從消息隊列中獲取的消息為空,則說明該消息隊列已經退出。

代碼第31行:這一行是重點,此處調用了當前消息的目標對象去分發消息。有1-1小節的最後一段我們知道,消息隊列的目標對象就是Handler對象,因此這裡就是調用Handler去調度消息或者叫分發消息。有此處也看出來,Message消息是有哪個Handler發送的,就有哪個Handler去分發消息。

跟蹤代碼進入

msg.target.dispatchMessage(msg);

以上方法在Handler類中

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

該方法就是處理系統消息的地方,以上代碼有三個分支,我們先看最後一個分支,就是執行handleMessage(msg);方法,跟蹤代碼:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

我擦,這麼簡單,就一個空方法?那怎麼處理消息呢?注釋已經告訴我們了,Handler的子類必須重寫該方法用來接收消息,也就是說最後的消息

處理邏輯是延伸給Handler類的之類了。相信讀者對該方法應該不陌生吧,就是我們經常在實現Handler類中重寫的那個方法,消息的處理就在這個方法裡面實現了。

到此,消息的調用/消息的分發就結束了,最後通過一個空方法將消息的處理邏輯留給了Handler的之類。

1-4消息的處理

消息有以上三步操作,最後進入消息的處理階段,這裡就到了我們非常熟悉的地方了,消息的處理是留給我們Handler子類去實現的,也就是我們平時編寫Handler消息那樣:

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

Handler消息執行過程總結:

下面用一張圖來描述消息的發送,消息的入列,消息的分發,消息的處理四個過程。

這裡寫圖片描述

有線程中的Handler把Message類封裝的消息發送到當前線程的Looper對象中的消息隊列MessageQueue中,然後Looper遍歷循環消息隊列MessageQueue,從消息隊列中取出消息,通過target Handler將消息分發出去,最後當前線程中的Handler獲取到該消息,並對消息進行處理。

Handler有關的構造方法

分析Handler類源碼發現,Handller類有很多個構造方法。愛思考的你肯定會問,這些構造方法有什麼不同呢?那麼我們就從代碼中來分析他們之間的不同吧:

2-1 不帶參的構造方法

 private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //接收消息之後,處理消息
        }
    };

我們一般在UI線程中這麼來實例化一個Handler對象,然後通過重寫Handler類中的handleMessage(Message msg)方法來處理消息。

2-2帶一個參數Callback的構造方法

 private Handler handler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //接收消息之後,處理消息
            return true;
        }
    });

此處的Callback參數是Handler類的內部回調接口,我們通過實現Callback接口中的handleMessage(Message msg)方法來處理消息。

2-3帶兩個參數的構造方法

 private Handler handler2 = new Handler(Looper.myLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //TODO 接收消息之後,處理消息
            return true;
        }
    });

第一個參數是當前線程的Looper對象,第二個參數是Callback接口,消息的處理和2-2小節一樣,唯一不同的地方就是多了一個Looper參數。

總結

由1-3小節我們發現,Handler類中的dispatchMessage方法分發消息有幾種情況:

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

1.而當你使用2-1小節的構造方法創建Handler對象時,就是采用上面代碼中的第三種處理消息的方式了,即重寫Handler類中的handleMessage(msg)方法。

2.當你使用2-2和2-3小節的構造方法創建Handler對象時,就采用上面代碼中的第二中處理消息的方式,即實現Handler類中的Callback接口中的

handleMessage(msg)方法。值得注意的是,如果Callback接口回調處理消息返回值是false的話,此消息還會調用第三種處理消息方式,因此沒有特殊需求,我們一般在實現Callback接口回調方法時都返回true。

3.那麼上面代碼第一種處理消息的方式handleCallback(msg)是什麼時候觸發的呢?上面代碼加了一個if條件判斷,只有當msg消息中的成員變量

callback不為空時才調用該方式處理消息。那什麼時候callback不為空呢?帶著這個問題繼續分析Handler發送消息的方法。

Handler發送消息的方法

handler發送消息的方法有多達11種,一聽嚇一跳,這麼多方法怎麼區分?不用怕,我把這些方法分為如下兩類。

send系列 post系列

3-1 send系列

         private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
            //接收消息之後,處理消息
            }
        };

        Message msg = mHandler.obtainMessage();

        //發送一個消息碼為0的空消息
        mHandler.sendEmptyMessage(0);
        //發送一個消息碼為0,絕對時間點為1000ms的空消息,該時間應該大於等於當前時間,下同
        mHandler.sendEmptyMessageAtTime(0, 1000);
        //發送一個消息碼為0,延時時間為1000ms的空消息
        mHandler.sendEmptyMessageDelayed(0, 1000);
        //發送一個沒有延時的msg封裝的消息
        mHandler.sendMessage(msg);
        //發送一個絕對時間點為1000ms的msg封裝的消息
        mHandler.sendMessageAtTime(msg, 1000);
        //發送一個延時時間為1000ms的msg封裝的消息
        mHandler.sendMessageDelayed(msg, 1000);
        //立即發送一個msg封裝的消息到消息隊列的最前端
        mHandler.sendMessageAtFrontOfQueue(msg);

分析源碼你會發現,除了最後一個方法,以上所有的方法最後都會調用如下方法:

/**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) uptimeMillis.
     * The time-base is {@link android.os.SystemClock#uptimeMillis}.
     * 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);
    }

分析:以上注釋都解釋的很清楚,第一個參數就是Message類封裝的消息,第二個參數就是消息的更新時間,該時間是絕對時間。

3-2 post系列

        mHandler = new Handler();

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                //TODO 接收消息之後,處理消息
            }
        });
        mHandler.postAtTime(new Runnable() {
            @Override
            public void run() {
                //TODO 接收消息之後,處理消息
            }
        }, new Object(), 1000);

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //TODO 接收消息之後,處理消息
            }
        }, 1000);

        mHandler.postAtFrontOfQueue(new Runnable() {
            @Override
            public void run() {
                //TODO 接收消息之後,處理消息
            }
        });

分析:和send系列大有不同,此處我們的Handler無需在去重寫handlerMessage方法來處理消息了,此處將接口類Runnable作為消息發送,然後實現Runnable接口類中的run方法來處理消息。對於這一點我相信很多初學者會有疑問,這裡又沒有Message封裝消息,怎麼將Runnable接口類當作消息發送呢? 一句話,還是跟蹤源碼看看:

  /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by uptimeMillis.
     * The time-base is {@link android.os.SystemClock#uptimeMillis}.
     * Time spent in deep sleep will add an additional delay to execution.
     * The runnable will be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
     * 
     * @return Returns true if the Runnable 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 Runnable will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *         
     * @see android.os.SystemClock#uptimeMillis
     */
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

分析:不管post系列調用哪個方法,最終都會調用上面這個方法來發送消息,你會驚奇的發現,原來post系列也是調用send系列的方法發送方法的。只不過此處調用了getPostMessage方法將Runnable對象轉換成Message對象而已。跟蹤代碼進入getPostMessage方法:

private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }

看到沒?調用此方法,將Runnable對象r賦值給了Message類中的clalback成員變量。現在我們回顧一下2-3小節Handler類中的

dispatchMessage方法處理消息的三種方式,此處將Runnable接口對象賦值之後Message類中的msg.callback就不會為空,因此調用第一種處理消息的方式。

第3節總結:

不管3-1或者3-2小節調用那種方式發送消息,最終都會調用Handler類中的enqueueMessage成員方法,將消息按照消息更新時間順序插入到消息隊列中,這個過程的分析可以參考1-1小節最後一段內容。

在Message消息封裝者類中有這麼一段注釋:

 /*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.

 

*/

這段話告訴我們,當你需要構造一個Message消息對象的時候,最好的方法是調用Message.obtain()或者Handler.obtainMessage()方法來獲得,而不是這樣獲得:

 Message msg1 = new Message();

為什麼這樣獲得Message對象不好?我們跟蹤源碼瞧瞧不就知道了。

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

這是一個Message類中的靜態方法,方法開頭解釋的很清楚:從全局的消息池中獲得一個新的消息示例,避免用戶多次創建Message消息實例浪費

內存。只有當全局的消息池中沒有可用的消息實例,才去調用new 一個新的Message消息實例。總之,歸納成一句話,調用Message.obtain()或

者Handler.obtainMessage()方法避免多次創建Message實例,這樣性能更加優化。

Handler移除消息的方法以及Handler內存洩漏問題

方法包括如下:

//從消息隊列中移出post系列方法中Runnable對象的消息
 mHandler.removeCallbacks(Runnable r);
 //從消息隊列中移除消息碼為“what”的消息
 mHandler.removeMessages(int what);
 //從消息隊列中移除所有post系列方法和send系列方法發送的消息
 mHandler.removeCallbacksAndMessages(Object token);

可能很初學者會有疑問,開發中很少用到以上這些方法。那如果你很少使用以上方法,說明你還是一個Android小白。如果不調用以上相應的方法

去移除消息,就會存在Handler對象內存洩漏的隱患。此話怎講?我在一次項目開發中遇到這樣的問題:

我開啟一個新的工作線程WorkThread去執行一段耗時網絡請求,當請求結束之後我調用UI線程中的Handler發送一個Message消息給UI線程,但

是此時,我早已經退出當前持有Handler對象的Activity,這會持有Handler的Activity是接收不到WorkThread發送過來的消息的,因為

Activity已經退出,因此會出現一個bug:Handler對象內存洩露。那怎麼解決這個Handler內存洩露隱患呢?

4-1

很簡單,在持有Handler對象的Activity的onDestory方法調用如下代碼去移除消息隊列中的所有消息

mHandler.removeCallbacksAndMessages(null);

此方法只要傳遞一個null空參數就表示移出當前線程中消息隊列中所有的消息。有些人可能會說,我並不想清除消息隊列,可能還有其他Activity等待接收消息,好辦。你可以調用如下相應的方法移除消息隊列中相應的消息:

//從消息隊列中移出post系列方法中Runnable對象的消息
 mHandler.removeCallbacks(Runnable r);
 //從消息隊列中移除消息碼為“what”的消息
 mHandler.removeMessages(int what);

4-2

那麼我們是否還有其他辦法解決Handler內存洩漏問題呢?答案是肯定的:將Handler申明為靜態的,因為靜態類不持有外部類的引用。

public class SubActivity extends Activity {
  static class StaticHandler extends Handler {
        WeakReference mActivityReference;

        StaticHandler(Activity activity) {
            mActivityReference= new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            final SubActivity activity = (SubActivity) mActivityReference.get();
            if (activity != null) {
                activity.textView.setText(測試靜態Handler解決內存洩漏問題);
            }
        }
    }
}

UI線程在使用Handler之前不需要Looper.prepare的原因

文章一開頭由“一個Bug引發的思考”我們知道,在線程中使用Handler之前需要調用Looper.prepare方法。愛思考的你會很納悶,那為什麼我

們的UI線程在創建Handler對象的時候沒有調用Looper.prepare方法呢?此時我們不得不從UI線程是怎麼來的說起了。不過這裡不展開討論,直接給出答案,我們創建一個Application的時候,都是由系統的一個ActivityThread類來啟動的。跟蹤代碼進入ActivityThread類:

public final class ActivityThread {
...................

    public static void main(String[] args) {
            ..............
            Looper.prepareMainLooper();
            ..............
    }
}

該類中有一個main方法,是不是感覺很熟悉啊?對了,這裡就是我們整個Application應用的入口了。在main方法中調用了Looper.prepareMainLooper(),猜測該方法裡面就調用了Looper.prepare方法來為線程循環loop准備。跟蹤源碼:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException(The main Looper has already been prepared.);
            }
            sMainLooper = myLooper();
        }
    }

果然,裡面調用了prepare方法。到此,我們就知道其實在UI線程中系統在ActivityThread類中就幫我們調用了Looper.prepare方法為UI線程loop循環做准備。

HandlerThread幫你創建循環消息處理機制

你可能會遇到這樣一種情況,在UI線程中創建一個工作線程WorkThread執行耗時任務。在正常情況下,這個WorkThread執行完任務之後就銷毀

了。但是你過了一段時間又有一個耗時任務需要執行,這會你會怎麼辦?你只能重新去創建一個WorkThread執行耗時任務。但是線程Thread的每

次創建和銷毀都是很耗系統資源的,因此在這種情況下,我們的HandlerThread就此誕生了。HandelrThread是繼承自Thread實現了,

也就是說HandlerThread是一個線程。只是該線程裡面幫你實現了Looper循環機制,從而使得該線程變成帶有循環機制的線程。言外之

意:當該線程執行完一個耗時任務之後不會馬上被銷毀,除了用戶主動調用HandlerThread類中的quit成員方法來退出當前loop循環。關於

HandlerTread是怎麼使用的?可以參考我的另一篇博客:
Android HandlerThread 源碼分析

總結:

通過以上文章分析,基本了解了Android系統的消息處理機制是怎麼一回事,也知道了怎麼使用Handler,以及Handler使用的一些陷阱等。來總結一下 Handler,Message,Looper,MessageQueue,Thread之間的關聯吧。

如果一個線程Thread想要使用Handler類來發送消息,就必須先調用Looper類中的prepare成員方法來做一些准備工作,然後調用Looper類

中的loop方法啟動該線程的循環機制。Handler類把封裝成Message類的消息發送到Looper類中的消息隊列中,然後Looper類中的loop循環方法

中分發消息,最後將相應的消息對象分發到相應的target handler類中去處理消息。

由此我們知道:一個線程Thread中只能擁有一個Looper對象,且一個Looper對象中只能擁有一個MessageQueue消息隊列,但是一個Thread

線程中可以有多個Handler對象,而多個Handler對象共享同一個MessageQueue消息隊列。最後提供一個Handler消息處理機制的流程圖

這裡寫圖片描述

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved