Android Handler 詳解

讀前思考

學習一門技術或者看一篇文章最好的方式就是帶着問題去學習,這樣才能在過程當中有茅塞頓開、燈火闌珊的感受,記憶也會更深入。android

  1. 如何獲取 Message 進行消息發送
  2. 使用 handler 形成內存泄露,如何避免?
  3. handler looper messageQueue的關係
  4. 程序是如何區分是將消息發給哪一個 Handler ?

概述

作 Android 開發確定離不開跟 Handler 打交道,它一般被咱們用來作主線程與子線程之間的通訊工具,而 Handler 做爲 Android 中消息機制的重要一員也確實給咱們的開發帶來了極大的便利。git

能夠說只要有異步線程與主線程通訊的地方就必定會有 Handlerc#

一、 Handler 的基本使用

1.1 建立 Handler

Handler 容許咱們發送延時消息,若是在延時期間用戶關閉了 Activity,那麼該 Activity 會泄露bash

這個泄露是由於 Message 會持有 Handler,而又由於 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就致使 Activity 泄露。異步

解決該問題的最有效的方法是:將 Handler 定義成靜態的內部類,在內部持有 Activity 的弱引用,並及時移除全部消息async

示例以下:ide

public class Part8HandlerActivity extends AppCompatActivity {

    private Button bt_handler_send;

    private static class MyHandler extends Handler {

        //弱引用持有Part8HandlerActivity , GC 回收時會被回收掉
        private WeakReference<Part8HandlerActivity> weakReference;

        public MyHandler(Part8HandlerActivity activity) {
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Part8HandlerActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null != activity) {
                //執行業務邏輯
                Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_part8_handler);

        //建立 Handler
        final MyHandler handler = new MyHandler(this);

        bt_handler_send = findViewById(R.id.bt_handler_send);
        bt_handler_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //使用 handler 發送空消息
                        handler.sendEmptyMessage(0);

                    }
                }).start();
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        //移除全部回調及消息
        myHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}
複製代碼

注意:單純的在 onDestroy 移除消息並不保險,由於 onDestroy 並不必定執行。函數

1.2 Message 獲取

獲取 Message 大概有以下幾種方式:工具

Message message = myHandler.obtainMessage(); //經過 Handler 實例獲取
Message message1 = Message.obtain();    //經過 Message 獲取
Message message2 = new Message();       //直接建立新的 Message 實例
複製代碼

經過查看源碼可知,Handler 的 obtainMessage() 方法也是調用了 Message 的 obtain() 方法oop

public final Message obtainMessage()
{
    return Message.obtain(this);
}
複製代碼

經過查看 Message 的 obtain 方法

public static Message obtain(Handler h) {
        //調用下面的方法獲取 Message
        Message m = obtain();
        //將當前 Handler 指定給 message 的 target ,用來區分是哪一個 Handler 的消息
        m.target = h;

        return m;
    }
    
//從消息池中拿取 Message,若是有則返回,不然建立新的 Message
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,使用前兩種方式進行建立。

1.3 Handler 發送消息

Handler 提供了一些列的方法讓咱們來發送消息,如 send()系列 post()系列 。

不過無論咱們調用什麼方法,最終都會走到 MessageQueue.enqueueMessage(Message,long) 方法。 以 sendEmptyMessage(int) 方法爲例:

//Handler
sendEmptyMessage(int)
  -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
      -> enqueueMessage(MessageQueue,Message,long)
  			-> queue.enqueueMessage(Message, long);

複製代碼

從中能夠發現 MessageQueue 這個消息隊列,負責消息的入隊,出隊。

二、Handler 原理解析

1. 構造函數

實際上咱們在實例化 Handler 的時候 Handler 會去檢查當前線程的 Looper 是否存在,若是不存在則會報異常,也就是說在建立 Handler 以前必定須要先建立 Looper

public Handler(Callback callback, boolean async) {
     //檢查當前線程是否持有 Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //Looper 持有一個 MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

2. Looper

經過調用 Looper.prepare() 能夠在當前線程建立 Looper,而後調用 Looper.loop() 讓消息隊列循環起來。

代碼以下:

private static void prepare(boolean quitAllowed) {
    
    //若是當前線程已經存在 Looper,則會拋出異常
    //在主線程中調用 prepare 就會拋出此異常,由於主線程已經存在 Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //將建立的 Looper 對象存入線程的 ThreadLocal 中,保持惟一
    sThreadLocal.set(new Looper(quitAllowed));
}
複製代碼

Looper.loop() 相關代碼

public static void loop() {
        //會先獲取當前線程的 Looper,若是不存在拋出異常
        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;

        //..
        for (;;) {
            //不斷從 MessageQueue 中獲取消息
            Message msg = queue.next(); // might block
            //
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //..
            try {
            // 經過 handler 發送消息
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                //..
            }
            //..
            //回收 Message
            msg.recycleUnchecked();
        }
    }
複製代碼

3. 小結

Handler 的背後有着 Looper 以及 MessageQueue 的協助,三者通力合做,分工明確。 嘗試小結一下它們的職責,以下:

Looper :負責關聯線程以及消息的分發在該線程下從 MessageQueue 獲取 Message,分發給 Handler ;
MessageQueue :是個隊列,負責消息的存儲與管理,負責管理由 Handler 發送過來的 Message ;
Handler : 負責發送並處理消息,面向開發者,提供 API,並隱藏背後實現的細節。

Handler 發送的消息由 MessageQueue 存儲管理,並由 Loopler 負責回調消息到 handleMessage()。

線程的轉換由 Looper 完成,handleMessage() 所在線程由 Looper.loop() 調用者所在線程決定。

三、 總結

Handler 簡單易用的背後藏着工程師大量的智慧,要努力向他們學習。

但願看完本文能加深你對 Handler 的理解,對接下來學習有所幫助。

四、 參考

Handler

Android消息機制1-Handler(Java層)

Handler 都沒搞懂,拿什麼去跳槽啊?

相關文章
相關標籤/搜索