Android之Handler消息機制——深入理解 Looper、Handler、Message、MessageQueue

序言

handler是我們日常編碼中經常使用的一個類,通常用來由子線程轉到主線程,或子線程與子線程之前的通信(消息傳遞),那麼什麼是Looper呢?什麼是MessageQueuene呢?不要着急,我們一步一步看!

1.示例

一般我們編碼最基本的常識就是,不能在主線程執行耗時操作(如網絡請求、讀取數據、數據庫讀寫、io流操作等等),必須創建一個子線程去執行,如以下示例:

Android消息機制原理——爲什麼不能在子線程更新UI?

   //運行在子線程
        Thread twoThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = mHandler.obtainMessage();
                message.obj = "handler";
                mHandler.sendMessage(message);
                Log.i("TAG" , "當前線程twoThread:" + Thread.currentThread().getName());
            }
        });


        twoThread.setName("thread#2");
        twoThread.start();

耗時操作可以放在run方法中去執行,並且可以設定指定的線程名,但是我們執行完耗時操作拿到數據之後,需要回到主線程去更新UI,但是子線程不能更新UI,所以我們需要轉到主線程去進行UI更新操作,就需要用到handler,如以下示例:

 Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

我們在子線程中獲取到數據之後,通過sendMessage來發送數據,然後在handler中接收數據進行處理,基本的使用就完成了,但是我們僅僅知道使用是不夠的,我們要了解其中的原理,並且能夠在未來的編碼中使用這種模式。

到這裏我們就有一個問題了,爲什麼handler能夠接收子線程發送的Message?我們可以通過源碼解析來進行分析!

2.源碼解析

1.Looper

一般looper有兩個常用的方法Looper.prepare()和Looper.loop()

Looper.prepare():用來創建一個Looper對象,並把Looper對象存儲到sThreadLocal對象中

Looper.loop():用來輪詢,假如有新的消息,就將新的消息添加到消息隊列中;假如沒有消息,就阻塞線程

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//prepare()方法用來創建一個Looper
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));
}
  • 在prepare()方法中,先創建了一個ThreadLocal對象,相當於一個數組或者一個HashMap,用來存儲當前線程的消息,使用ThreadLocal的好處是可以保證當前拿到的消息是這個線程的消息,可以避免消息錯亂!
  • 假如sThreadLocal已經有Looper對象了,將會報錯!因爲已經創建過的Looper對象不能重複添加,用來保證唯一性!這也說明了prepare()方法只能調用一次!
  • 將創建的looper對象添加到sThreadLocal中

接下來我們看一下創建Looper的方法

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
}
  • mQueue:消息隊列,用來保存發送的消息
  • mRun :是否運行
  • mThread:當前所在的線程

創建完Looper之後,我們會調用Looper.loop()方法,loop()方法重點代碼如下:

public static void loop() {
    //獲取Looper對象
    final Looper me = myLooper();
    //Looper對象不能爲空,即必須先創建
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //從消息隊列中獲取消息列表
    final MessageQueue queue = me.mQueue;
    //開始進行輪詢
    for (;;) {
        //遍歷所有的消息
        Message msg = queue.next(); // might block
        //假如沒有消息,就進行阻塞
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        /* 
         * 如果有消息了,就通過dispatchMessage()方法進行發送消息,消息將在dispatchMessage()中 
         * 進行處理
         */
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //釋放資源
        msg.recycleUnchecked();
    }
}

我們一步一步看,首先獲取Looper對象,那麼Looper對象從哪裏來呢?我們還記得在調用Looper.prepare()方法時創建了一個Looper對象,並把它set到sThreadLocal中,那麼我們的Looper對象也很有可能是在sThreadLocal中取的,我們看一下myLooper()方法;

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

果然如此!上面的註釋已經寫得很明白了,我們總結一下loop方法到底做了什麼!

(1)獲取Looper對象,並且從消息隊列獲取消息

(2)無限循環消息,如果沒有新的消息,就阻塞線程;如果有新的消息,就通過dispatchMessage()方法處理

(3)釋放資源

msg.target返回的是一個handler對象,這個時候,我們的handler和message已經開始產生關聯了!接下來就到我們的主角Handler上場了!

2.Handler

回到最開始我們的示例代碼

  Message message = mHandler.obtainMessage();
  message.obj = "handler";
  mHandler.sendMessage(message);

我們從子線程轉到主線程時,是通過handler來傳輸消息,首先獲取了Message的單例對象,通過sendMessage方法來發送,那麼最重要的肯定是sendMessage方法了

   public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

sendMessage()方法最後調用enqueueMessage()方法來解析數據

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

我們爲msg.target賦了值,在以上我們的Looper.loop()方法最後處理是調用msg.target.dispatchMessage(msg),在sendMessage時,msg.target被賦值!接下來我們看一下dispatchMessage()方法

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我們可以看到很熟悉的一個方法handlerMessage()

public void handleMessage(Message msg) {
}

handlerMessage()竟然是一個空方法,仔細一想確實,我們看下面一串代碼就知道了

 Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

handlerMessage()方法是用來給我們實現的,在handler中重寫父類方法,拿到Message來進行接下來的操作!

我們再回頭看一下handler的構造方法

public Handler() {
        this(null, false);
}
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());
            }
        }
 
        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;
    }

在handler的構造方法中,通過獲取對應且唯一的Looper來傳遞消息,當然,一定要調用Lopper.prepare()方法來創建Looper!完整的消息傳遞機制就顯而易見了!

總結

(1)開啓線程,調用Looper.prepare()方法創建一個Looper,Looper.prepare()只能調用一次,同時將創建一個MessageQueue(消息隊列),用來存儲消息

(2)調用Looper.loop()方法來獲取Lopper對象和MessageQueue消息隊列,並開啓無限循環,讀取消息隊列的消息,如果消息隊列裏沒有消息,則進行阻塞;如果有新的消息,則通過dispatchMessage傳遞給handler

(3)創建handler接收子線程發送過來的消息,通過dispatchMessage()方法拿到Message,通過重寫父類方法handlerMessage()最終拿到數據

 

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