Android Handler 消息機制詳述

第一次在掘金上發表文章,生怕說錯了什麼誤人子弟,更不敢標題黨。
若是文章有誤,歡迎您直接在評論指出。java

一. What、Handler 是什麼

Handler 與 Message、MessageQueue、Looper 一塊兒構成了 Android 的消息機制,Android 系統經過大量的消息來與用戶進行交互,View 的繪製、點擊事件、Activity 的生命週期回調等都做爲消息由主線程的 Handler 來處理。android

Handler 在消息機制中的做用是:發送和處理消息。git

Handler 還有另外一個重要的做用,跨線程通訊。最多見的就是子線程請求網絡,而後使用 Handler 將請求到的數據 post 到主線程刷新 UI,大名鼎鼎的 Retrofit 也是這麼作的。網絡

二. How、如何使用 Handler

  • 建立 Handler 併發

    private Handler handler = new Handler() {
      // 重寫 handleMessage 來根據不一樣 what 來處理 Message
      // 這個方法在 Handler 建立的線程執行
      @Override public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                  MLog.i(msg.obj);
                  break;
                case 1:
                  break;
                default:
    
            }
        }
    };複製代碼
  • 建立併發送 Message異步

    // 獲取一個 Message
    Message message = Message.obtain();
    message.what = 0;
    message.obj = new Object();
    // 使用 Handler 發送 Message
    // 消息發送完成後 Handler 的 handleMessage(Message msg) 會處理消息
    handler.sendMessage(message);
    
    // 延遲 1s 發送 Message
    handler.sendMessageDelayed(message, 1000);
    // 發送一個空的 Message
    handler.sendEmptyMessage(msg.what);  
    // 延遲發送一個空的 Message
    handler.sendEmptyMessageDelayed(0, 1000);
    
    // 還能夠這樣
    // 建立 Message 並綁定 Handler
    Message message = handler.obtainMessage();
    message.what = 0;
    message.obj = new Object();
    
    // 發送 Message
    message.sendToTarget();複製代碼
  • 使用 Handler 子線程請求數據,主線程刷新 UIasync

    // 1. 在主線程建立 Handler(略)
    // 2. 子線程請求數據,主線程刷新 UI
    new Thread(new Runnable() {
        @Override public void run() {
            // 獲取網絡數據
            final List<Object> datas = getNetData();
    
            // 方法一:將數據做爲 Message 的 obj 發送出去,在 handleMessage 中刷新 UI
            Message msg = Message.obtain();
            msg.what = 1;
            msg.obj = data;
            handler.sendMessage(msg);
    
            // 方法二:直接在 post 中刷新 UI
            handler.post(new Runnable() {
                @Override public void run() {
                  // 使用 datas 刷新 UI
                  // 這個方法也會在 Handler 建立的線程執行
                }
            });
        }
    }).start();複製代碼

三. Handler 的內存泄漏

不得不說,上面使用 Handler 的方法會有內存泄漏的風險ide

  • Handler 內存泄漏的兩個緣由函數

    • Java 中非靜態內部類和匿名內部類會持有外部類的引用oop

      // 這是一個外部類 Handler 不會持有外部類引用
      // 顯然 handleMessage 沒地方寫了
      Handler handler = new Handler();
      
      // 重寫 handleMessage 後將獲得一個內部類 Handler,之內 handleMessage 是在外部類中實現的
      // 它持有外部類引用,可能會引發內存泄漏
      Handler handler = new Handler() {
        @Override public void handleMessage(Message msg) {
              super.handleMessage(msg);
              switch (msg.what) {
                  case 0:
                    MLog.i(msg.obj);
                    break;
                  case 1:
                    break;
                  default:
      
              }
          }
      };
      
      // 這裏 Handler 是一個匿名類,但不是內部類
      // Runnable 是一個匿名內部類,持有外部類引用,可能會引發內存泄漏
      new Handler().post(new Runnable() {
          @Override public void run() {
            // ...
          }
      });複製代碼

    • Handler 的生命週期比外部類長。

  • 分析

    • 非靜態的內部 Handler 子類、匿名 Handler 子類會持有外部類的引用(Activity),而 Handler 可能會由於要等待處理耗時操做致使存活時間超過 Activity,或者消息隊列中存在未被 Looper 處理的 Message ,而 Message 會持有 Handler 的引用。因而,在 Activity 退出時,其引用仍是被 Handler 持有,致使 Activity 沒法被及時回收,形成內存泄露。
    • 非靜態的內部 Runnable 子類、匿名 Runnable 子類 post 到任意 Handler 上時,Runnable 實際上是 Massage中的 Callback,持有 Message 引用,若是這個 Massage 在消息隊列尚未被處理,那麼就會形成 Runnable 一直持有外部類的引用而形成內存泄露。
  • 解決方案:

    • 經過靜態內部類或者外部類來聲明 Handler 和 Runnable。
    • 經過弱引用來拿到外部類的變量。
    • 在 Activity/Fragment 銷燬的時候請空 MessageQueue 中的消息。
  • 代碼

    // Handler 弱引用封裝
    public class SafetyHandler<T> extends Handler {
        /** * 外部引用, 例如 Activity, Fragment, Dialog, View 等 */
        private WeakReference<T> mTargetRef;
        public SafetyHandler() {
        }
        public SafetyHandler(T target) {
            this.mTargetRef = new WeakReference<>(target);
        }
    
        public T getTarget() {
            if (isTargetAlive()) {
                return mTargetRef.get();
            } else {
                removeCallbacksAndMessages(null);
                return null;
            }
        }
    
        public void setTarget(T target) {
            this.mTargetRef = new WeakReference<>(target);
        }
    
        private boolean isTargetAlive() {
            return mTargetRef != null && mTargetRef.get() != null;
        }
    }
    
    // 在 Fragment 中使用方法
    // 想重寫 handleMessage 的話,要建立靜態內部類或者外部類,不然有內存泄漏風險
    private static class MyHandler extends SafetyHandler<MyFragment> {
    
        MyHandler(MyFragment fragment) {
              super(fragment);
        }
    
        @Override public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(getTarget() != null) {
                MyFragment fragment = getTarget();
                switch (msg.what) {
                   // 操做 fragment
                }
            }
        }
    }
    
    // 聲明 Handler
    MyHandler handler = new MyHandler(this);
    
    // 使用 Handler
    handler.sendMessage() ...
    
    // onDestroy 
    @Override public void onDestroy() {
          super.onDestroy();
          handler.removeCallbacksAndMessages(null);
    }複製代碼

四. Why、Handler 消息機制的原理

這部分從 ActivityThread 的 main 方法出發,打通整個消息機制的流程,結合源碼體驗效果更佳。

概述

介紹消息機制的原理前,咱們先來看一下 Handler 與 Message、MessageQueue、Looper 這個四個類的做用

  • Handler:前面已經說過,Handler 負責發送和處理 Message。
  • Message:消息,負責傳遞標示(what) 和數據(obj) ;每一個 Message 都會經過 target 這個成員變量來綁定一個 Handler,由這個 Handler 來發送和處理 Message。
  • MessageQueue:消息隊列,負責存放有 Handler 發送過來的消息;每一個 Handler 中都有一個 final MessageQueue mQueue,Handler 發送消息就是把消息加入這個 MessageQueue 。
  • Looper:負責不斷的從 MessageQueue 中取出消息而後交給 Handler(Message#target ) 處理;每一個 Looper 中都有一個惟一的消息隊列(final MessageQueue mQueue),每一個 Handler 中都有一個 final Looper mLooper,Handler 中的 MessageQueue 就是來自 Looper。

注意:每一個線程只能有一個 Looper 和 一個 MessageQueue,能夠有多個 Handler,每一個 Handler 能夠發送和處理多個 Message。

另外,提到消息機制就不得不說一下 Android 中的主線程(UI 線程)

Android 中的主線程經過 Looper.loop() 進入一個無線循環中,不斷的從一個 MessageQueue 取出消息,處理消息,咱們每觸發一個事件,就會向這個 MessageQueue 中添加一個消息,Looper 取出這個消息,Handler 處理這個消息,正是 Looper.loop() 在驅動着 Android 應用運行下去 ,這也是爲何 Looper.loop 爲何不會阻塞住主線程的緣由(固然前提是在 ActivityThread 的 main 函數 中調用)。

正式進入源碼分析

本源碼分析基於 API 25,如下源碼中刪除了一些無關的代碼

一、在主線程的入口,ActivityThread 的 main 方法

public static void main(String[] args) {
        // 準備主線程的 Looer 
        Looper.prepareMainLooper();
        // 建立 ActivityThread
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        // 獲取主線程的 Handler 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        // 對消息隊列進行無線輪詢,處理消息
        Looper.loop();
        // 一旦跳出循環,拋出異常(Android 不容許跳出主線程的 Looper.loop())
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }複製代碼

-> Looper.prepareMainLooper()

public static void prepareMainLooper() {
        // 準備一個 Looper
        prepare(false);
        synchronized (Looper.class) {
            // main Looper 只能初始化一次,再次初始化會拋出異常
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 獲取 main Looper
            sMainLooper = myLooper();
        }
    }複製代碼

-> prepare(false)

// 準備一個 Looper,quitAllowed 是否容許 Looper 中的 MessageQueue 退出
// 默認 prepare() 容許退出,主線程這裏不容許退出
private static void prepare(boolean quitAllowed) {
    // 先看下 sThreadLocal 是什麼
    // static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    // ThreadLocal:線程本地存儲區,每一個線程都有本地存儲區域,這個區域是每一個線程私有的,不一樣的線程不能之間不能彼此訪問
    // 若是 sThreadLocal 中有數據,拋出異常,換句話說 prepare() 這個函數每一個線程只能執行一次
    if (sThreadLocal.get() != null) {
          throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 建立 Looper 保存到該線程的 ThreadLocal 中
    sThreadLocal.set(new Looper(quitAllowed));
}複製代碼

-> new Looper(quitAllowed)

private Looper(boolean quitAllowed) {
    // 在 Looper 建立的時候建立一個消息隊列
    // quitAllowed:消息隊列是否能夠退出,主線的消息隊列確定不容許退出,因此上面是 prepare(false)
    // quitAllowed 爲 false 執行 MessageQueue#quit 退出消息隊列時會出現異常
    mQueue = new MessageQueue(quitAllowed);
    // 獲取 Looper 存在於哪一個線程
    mThread = Thread.currentThread();
}複製代碼

-> sMainLooper = myLooper()

public static @Nullable Looper myLooper() {
    // 從 sThreadLocal 中獲取當前線程的 Looper 
    // 若是當前線程沒有掉用 Looper.prepare 返回 null
    return sThreadLocal.get();
}複製代碼

-> sMainThreadHandler = thread.getHandler();

final Handler getHandler() {
    // 返回 mH
    return mH;
}

// mH 在成員變量的位置 new H()
final H mH = new H();

// H 繼承了 Handler 封裝了一系列關於 Acitivty、Service 以及其餘 Android 相關的操做
private class H extends Handler複製代碼

總結:在主線程的 main 方法中,會建立主線程的 Looper、MessageQueue,而後進入 Looper.loop() 循環中,不斷的取出消息,處理消息,以此來驅動 Android 應用的運行。

二、Handler 的建立,Handler 的全部構造方法都會跳轉到下面兩個之一

public Handler(Callback callback, boolean async) {
    // Hanlder 是匿名類、內部類、本地類時,若是沒有聲明爲 static 則會出現內存泄漏的警告
    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
    mLooper = Looper.myLooper();
    if (mLooper == null) {
          throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 消息隊列,從 Looper 中獲取
    mQueue = mLooper.mQueue;
    // 處理消息的回調接口
    mCallback = callback;
    // 處理消息的方式是否爲異步,默認同步
    mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}複製代碼

總結:在 Handler 的構造方法中,Handler 和 Looper、MessageQueue 綁定起來,若是當前線程沒有 Looper 拋出異常(這也是爲何直接在子線程建立 Handler 會出現異常)。

三、使用 Handler 發送消息

-> sendMessageAtTime(Message msg, long uptimeMillis)

// 除了 sendMessageAtFrontOfQueue,Handler 全部的 post、sendMessage 都會跳到這個方法
// Message msg: 要發送的消息
// long uptimeMillis: 發送消息的絕對時間,經過 SystemClock.uptimeMillis() 加上咱們本身的延遲時間 delayMillis 計算而來
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    // 消息隊列爲空(可能已經退出)返回 false 入隊失敗
    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);
}複製代碼

-> sendMessageAtFrontOfQueue(Message msg)

// 發送消息到 MessageQueeu 的隊頭
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
        this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 經過設置 uptimeMillis 爲 0,是消息加入到 MessageQueue 的隊頭
    return enqueueMessage(queue, msg, 0);
}複製代碼

-> enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

// 全部 Handler 的 post 、sendMessage 系列方法和 runOnUiThread 最終都會調用這個方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // msg.target 是一個 Handler,將 Message 和 Handler 綁定
    // 也就是用哪一個 Handler 發送消息,這個 Message 就和哪一個 Handler 綁定
    msg.target = this;
    // 若是設置了消息處理方式爲異步處理
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // MessageQueue 的方法,將消息入隊
    return queue.enqueueMessage(msg, uptimeMillis);
}複製代碼

-> MessageQueue#enqueueMessage(Message msg, long when)

boolean enqueueMessage(Message msg, long when) {
        // Messgae 沒有綁定 Handler 拋出異常
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // Messgae 正在使用 拋出異常
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            // 消息隊列正在退出,回收 Message
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();  // 調用 Message#recycleUnchecked() 
                return false;
            }
            msg.markInUse();  // 標記 Message 正在使用
            msg.when = when;  // 設置 Message 的觸發時間

            // mMessages 記錄着 MessageQueue 的隊頭的消息 
            Message p = mMessages;  
            boolean needWake;
            // MessageQueue 沒有消息、Message 觸發時間爲 0、Messgae 觸發時間比隊頭 Message 早
            // 總之這個 Message 在 MessageQueue 中須要最早被分發
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;     // 將之前的隊頭 Message 連接在這個 Message 後面
                mMessages = msg;  // 將這個 Message 賦值給 mMessages
                needWake = mBlocked;  // 隊列是否阻塞
            } else {
                // 標記隊列是否阻塞
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;

                // 按照時間順序將 Message 插入消息隊列
                for (;;) {
                    prev = p;   // prev 記錄隊頭
                    p = p.next; // p 記錄隊頭的後一個
                    // 隊頭後面沒有消息或者其觸發事件比要插入的 Message 晚,跳出循環
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 將 Message 插入隊列
                msg.next = p; 
                prev.next = msg;
            }

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

總結:到如今爲止,咱們的 Handler 已經將 Message 發送到了 MessageQueue,Message 靜靜的等待被處理。

四、Looper.loop() 還記得這個方法在 ActivityThread 的 main 調用了嗎?正是它在不斷處理 MessageQueue 裏面的消息。

public static void loop() {
        // 獲取 Looper.Looper.prepare 準備好的 Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 獲取 Looper 中的消息隊列
        final MessageQueue queue = me.mQueue;

        // 進入無線循環
        for (;;) {
            // 取出下一條消息
            Message msg = queue.next(); 

            // 沒有消息,退出 loop
            // 其實上面 queue.next() 也是一個無限循環,獲取到消息就返回,沒有消息就一直循環
            if (msg == null) {
                return;
            }

            try {
                // msg.target 實際上就是一個 Handler
                // 獲取到了消息,使用綁定的 Handler#dispatchMessage 分發消息
                msg.target.dispatchMessage(msg);
            } finally {

            }

            // 釋放消息,把 Message 的各個變量清空而後放進消息池中
            msg.recycleUnchecked();
        }
    }複製代碼

五、Handler#dispatchMessage(msg) 消息是如何處理的

public void dispatchMessage(Message msg) {
    // 1
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 2
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 3. 看到這個方法沒有!就是咱們建立 Handler 時重寫的 handleMessage
        // OK 整個流程打通!
        handleMessage(msg);
    }
}複製代碼

總結:流程雖然通了,可是處理 Message 的方法貌似有三種(我標記了序號),並且咱們的 handleMessage 的優先級最低,其餘方法會在什麼狀況下執行呢? 直接說結論了,調用 Handler 的 post 系列方法會走序號1的處理,建立 Handler 傳入 Callback 會走序號2 的處理。

Handler 機制總結:想使用 Handler 必需要有 Looper,建立 Looper 的時候會建立 MessageQueue,在 Handler 的構造的時候會綁定這個 Looper 和 MessageQueue,Handler 將 Message 發送到 MessageQueue 中,Looper.loop() 會不斷的從 MessageQueue 取出消息再交給這個 Handler 處理。

五. HandlerThread 的使用及源碼解讀

在子線程中能直接建立 Handler 嗎?

  • new Thread(new Runnable() {
        @Override public void run() {
            new Handler().post(new Runnable() {
                @Override public void run() {
                      MLog.i("Handler in " + Thread.currentThread().getName());
                }
            });
        }
    }).start();複製代碼
  • 答案前面提到了是不能,執行上面的代碼會出現 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 這個異常,異常提示咱們,不能再沒有調用 Looper.prepare() 的線程中建立 Handler。

  • 簡單修改下代碼就能夠了,給線程準備好 Looper

    new Thread(new Runnable() {
        @Override public void run() {
            // 準備一個 Looper,Looper 建立時對應的 MessageQueue 也會被建立
            Looper.prepare();
            // 建立 Handler 並 post 一個 Message 到 MessageQueue
            new Handler().post(new Runnable() {
                @Override public void run() {
                      MLog.i("Handler in " + Thread.currentThread().getName());
                }
            });
            // Looper 開始不斷的從 MessageQueue 取出消息並再次交給 Handler 執行
            // 此時 Lopper 進入到一個無限循環中,後面的代碼都不會被執行
            Looper.loop();
        }
    }).start();複製代碼
  • 上面的操做 Android 都幫咱們封裝好了,正是 HandlerThread 這個類。

HandlerThread 的簡單使用

// 1. 建立 HandlerThread
handlerThread = new HandlerThread("myHandlerThread") {
    // onLooperPrepared 這個方法子線程執行,由線程的 run 方法調用,能夠在裏面直接建立 Handler
    @Override protected void onLooperPrepared() {
        super.onLooperPrepared();
        new Handler().post(new Runnable() {
            @Override public void run() {
                // 注意:Handler 在子線程建立,這個方法也會運行在子線程,不能夠更新 UI
                  MLog.i("Handler in " + Thread.currentThread().getName());
            }
        });
    }
};

// 2. 準備 HandlerThread 的 Looper 並調用 onLooperPrepared
handlerThread.start();


// 3. 退出
@Override public void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}

// 也能夠這樣用
// 1. 建立 HandlerThread 並準備 Looper
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();

// 2. 建立 Handler 並綁定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
    @Override public void run() {
          // 注意:Handler 綁定了子線程的 Looper,這個方法也會運行在子線程,不能夠更新 UI
          MLog.i("Handler in " + Thread.currentThread().getName());
    }
});

// 3. 退出
@Override public void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}複製代碼

HandlerThread 源碼解讀

  • HandlerThread 繼承了 Thread,本質是一個擁有 Looper 的線程,所以在 HandlerThread 咱們能夠直接使用 Handler。

  • 構造方法

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    // 傳入線程的名稱和優先級
    // 注意 priority 的值必須來自 android.os.Process 不能來自 java.lang.Thread
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }複製代碼
  • run 方法:建立子線程的 Looper

    @Override
    public void run() {
        mTid = Process.myTid();
        // 準備一個 Looper
        Looper.prepare();
        synchronized (this) {
            // 獲取 Looper
            mLooper = Looper.myLooper();
            // Looper 獲取成功後,喚醒 getLooper 的 wait
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        // Looper 準備好的回調,在這個方法裏可使用 Handler 了
        onLooperPrepared();
        // Looper 開始循環取消息
        Looper.loop();
        mTid = -1;
    }複製代碼
  • getLooper 方法:獲取子線程的 Looper

    public Looper getLooper() {
        // 線程沒有開始或者死亡,返回 null
          if (!isAlive()) {
              return null;
        }
    
        // If the thread has been started, wait until the looper has been created.
        // Looper 的建立時在子線程完成的,而 getLooper 可能會在主線程調用
        // 當 Looper 沒有建立完成時,使用 wait 阻塞等待
        // 上面在 Looper 建立好後會 notifyAll 來喚醒 wait
         synchronized(this) {
            while (isAlive() && mLooper == null) {
                try {
                      wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }複製代碼
  • quit 和 quitSafely :結束 Looper 的運行

    // quit
    quit() -> looper.quit() -> mQueue.quit(false);
    // quitSafely 
    quitSafely() -> looper.quitSafely() -> mQueue.quit(true);
    
    // 這兩個方法最終都會調用到 MessageQueue 的 void quit(boolean safe) 方法
    // 前者會直接移除 MessageQueue 中的全部消息,而後終止 MessageQueue
    // 後者會將 MessageQueue 中已有消息處理完成後(再也不接收新消息)終止 MessageQueue複製代碼

    六.參考文章

相關文章
相關標籤/搜索