Handler源碼解析,深入淺出

Handler機(jī)制

Handler介紹:
我們都知道Android的主線程不能做耗時(shí)任務(wù),否者會(huì)導(dǎo)致ANR.但是界面的更新又必須在主線程中進(jìn)行,這樣我們就必須在子線程中處理耗時(shí)的任務(wù),然后在主線程中更新UI.


但是,我們?cè)趺粗雷泳€程何時(shí)完成任務(wù),又應(yīng)該什么時(shí)候更新UI,更新什么內(nèi)容,為了解決這個(gè)任務(wù),Android為我們提供了一個(gè)消息機(jī)制即Handler機(jī)制.


源碼分析:

創(chuàng)建Handler時(shí)無參構(gòu)造函數(shù)內(nèi)調(diào)用了兩個(gè)參數(shù)的構(gòu)造函數(shù),而在兩個(gè)參數(shù)的構(gòu)造函數(shù)中就是將一些變量進(jìn)行賦值.

通過Looper中的myLooper方法來獲得Looper實(shí)例的,如果Looper為null的話就會(huì)拋無法在未調(diào)用Looper.prepare()的線程內(nèi)創(chuàng)建handler異常

    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;

在調(diào)用Looper.myLooper()之前必須先調(diào)用Looper.prepare()方法

    public static void prepare() {
        prepare(true);//prepare()方法調(diào)用了prepare(boolean quitAllowed)方法
    }
    
    private static void prepare(boolean quitAllowed) {

        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //如果Looper為空,實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)sThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

從上面代碼中可以看到,prepare()方法調(diào)用了prepare(boolean quitAllowed)方法,實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)sThreadLocal中,由此可見Looper是唯一的,多次調(diào)用Looper.prepare()會(huì)報(bào)異常

什么是ThreadLocal:

Threadlocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其他線程來說無法獲取到數(shù)據(jù).


雖然在不同線程中訪問的是同一個(gè)ThreadLocal對(duì)象,但是它們通過ThreadLocal獲取到的值卻是不一樣的.


一般來說,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候,就可以考慮采用ThreadLocal

知道了ThreadLocal.set()方法的作用,則Looper.prepare()就是將Looper與當(dāng)前線程進(jìn)行綁定(當(dāng)前線程就是調(diào)用Looper.prepare方法的線程)

Looper.prepare()方法在主線程中Android系統(tǒng)已經(jīng)幫我們?cè)贏ctivityThread類的main方法中調(diào)用Looper.prepareMainLooper();從當(dāng)前線程中的ThreadLocal中取出Looper實(shí)例

    public static void prepareMainLooper() {
    
        prepare(false);//這里調(diào)用了prepare方法
        
        synchronized (Looper.class) {
            if (sMainLooper != null) {
            
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            
            sMainLooper = myLooper();
        }
    }

    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();//從當(dāng)前線程中的ThreadLocal中取出Looper實(shí)例
         }
    private Looper(boolean quitAllowed) {
    
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

通過上面的代碼,我們可以知道m(xù)Queue就是MessageQueue,在我們調(diào)用Looper.prepare方法時(shí)就將mQueue實(shí)例化了,拿到Looper中的mQueue這個(gè)成員變量,然后再賦值給Handler中的mQueue

mQueue = mLooper.mQueue;

Handler明明是在子線程發(fā)送的消息怎么會(huì)跑到主線程中?

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

其實(shí)Handler.sendMessage()、sendEmptyMessage等方法最終都是調(diào)用sendMessageAtTime(),返回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 = this; 就是將當(dāng)前的Handler對(duì)象賦值給了Message中的target變量,從而將調(diào)用sendMessage()方法的Handler與message進(jìn)行綁定.

queue.enqueueMessage(msg,uptimeMillis)就是調(diào)用了MessageQueue中的enqueueMessage(),將消息放入到消息隊(duì)列中,如果消息成功放入消息隊(duì)列,返回true,失敗返回false,而失敗的原因通常是因?yàn)樘幚硐㈥?duì)列正在退出.

Handler在sendMessage時(shí)會(huì)將自己設(shè)置給Message的target變量即將自己與發(fā)送的消息綁定。Handler的sendMessage是將Message放入MessageQueue中。


怎樣從MessageQueue中獲取Message

在ActivityThread類main()中,結(jié)尾處有Looper.loop();

    public static void loop() {
        final Looper me = myLooper();//通過myLooper方法拿到與主線程綁定的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        
        final MessageQueue queue = me.mQueue;//從Looper中得到MessageQueue
        
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
    
         //開始死循環(huán)
        for (;;) {
                //從消息隊(duì)列中不斷取出消息
                Message msg = queue.next(); // might block
             if (msg == null) {
                // No message indicates that the message queue is quitting.
             return;
        }
    
        try {
            //這句代碼是重點(diǎn)
            msg.target.dispatchMessage(msg);

Looper.loop();就是拿到用與主線程綁定的Looper對(duì)象,從Looper對(duì)象中得到MessageQueue,死循環(huán)從queue.next()不斷取出消息,若隊(duì)列中無消息,則阻塞等待.


msg.target.dispatchMessage(msg);在msg.target就是發(fā)送消息的那個(gè)Handler,所以這句代碼本質(zhì)就是調(diào)用了Hadler的dispatchMessage()方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
                handleCallback(msg);
        } else {
            //if中的代碼其實(shí)是和if (msg.callback != null) {handleCallback(msg);} 
            //原理差不多的,只不過mCallback是Handler中的成員變量。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                        return;
                }
            }
                //當(dāng)上面的條件都不成立時(shí),就會(huì)調(diào)用這句代碼
                handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    

msg.callback就是Runnable對(duì)象,當(dāng)msg.callback不為null時(shí)會(huì)調(diào)用 handleCallback(msg)方法

那什么情況下if (msg.callback != null)這個(gè)條件成立呢!就是調(diào)用Handler的post方法呀,最后在Runnable的run方法中來處理

    public final boolean post(Runnable r) {
            return  sendMessageDelayed(getPostMessage(r), 0);
    }

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

post方法最終也是將Runnable封裝成消息,然后將消息放進(jìn)MessageQueue中

    public void handleMessage(Message msg) {
        //它就是一個(gè)空方法,具體的代碼是我們實(shí)例化Handler重寫handleMessage()這個(gè)方法進(jìn)行處理的
    }

總結(jié)

Hanler機(jī)制主要是四個(gè)類

  • Handler :負(fù)責(zé)發(fā)送和處理消息
  • Message:用來攜帶數(shù)據(jù)
  • MessageQueue:消息隊(duì)列
  • Looper:消息泵,負(fù)責(zé)不斷的從MessageQueue中取Message
image
Handler
  1. 在實(shí)例化Handler的時(shí)候要先調(diào)用Looper.prepare()

Looper.prepare()方法就是實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)ThreadLocal中,將Looper與當(dāng)前線程綁定


但是,在主線程中Android系統(tǒng)已經(jīng)幫我們調(diào)用了Looper.prepare方法可以看下ActivityThread類中的main方法


Looper.prepareMainLooper();這句話的實(shí)質(zhì)就是調(diào)用了Looper的prepare方法

  1. 在實(shí)例化Handler的時(shí)候,通過Looper.myLooper()獲取Looper,然后在獲取Looper中的MessageQueue.

Looper.myLooper()就是從ThreadLocal中獲取Looper對(duì)象,Looper構(gòu)造中創(chuàng)建MessageQueue

  1. 在子線程中調(diào)用Handler的sendMessage方法時(shí)會(huì)將自己設(shè)置給Message的target變量即將自己與發(fā)送的消息綁定,將Message放入MessageQueue中,然后調(diào)用Looper.loop()方法來從MessageQueue中取出Message,執(zhí)行msg.target.dispatchMessage(msg)

Handler的無論時(shí)sendMessage、sendEmptyMessage等方法最后調(diào)用了sendMessageAtTime()方法,sendMessageAtTime()方法最后返回的是enqueueMessage()


enqueueMessage()方法最后是MessageQueue中的enqueueMessage方法,將消息放進(jìn)消息隊(duì)列中,如果消息已成功放入消息隊(duì)列,則返回true。失敗時(shí)返回false,而失敗的原因通常是因?yàn)樘幚硐㈥?duì)列正在退出。

  1. 通過Looper.loop()從MessageQueue中取出消息

ActivityThread類中的main方法結(jié)尾處,Looper.loop()方法,通過myLooper()方法拿到與主線程綁定的Looper,從Looper中得到MessageQueue,通過next()方法獲取Message,沒有消息時(shí)阻塞


msg.target.dispatchMessage(msg);說明已經(jīng)從消息隊(duì)列中拿到了消息,msg.target也就是發(fā)送消息的那個(gè)Handler,所以這句代碼的本質(zhì)就是調(diào)用了Handler中的dispatchMessage(msg)方法


dispatchMessage(msg)中判斷是post()還是send(),如果是post()就是調(diào)用Runnable中的run()方法,如果是send()及調(diào)用handlerMessage(Message msg)方法,handlerMessage()方法是空方法,自己重寫進(jìn)行處理


在子線程中使用Handler

在子線程中使用Handler的方式如下

    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();
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 【Android Handler 消息機(jī)制】 前言 在Android開發(fā)中,我們都知道不能在主線程中執(zhí)行耗時(shí)的任務(wù)...
    Rtia閱讀 5,091評(píng)論 1 28
  • 前言 在Android開發(fā)的多線程應(yīng)用場(chǎng)景中,Handler機(jī)制十分常用 今天,我將手把手帶你深入分析Handle...
    BrotherChen閱讀 532評(píng)論 0 0
  • 為了更好的理解 Looper 的工作原理,我們需要對(duì) ThreadLocal 進(jìn)行了解,如果對(duì) ThreadLoc...
    墨染書閱讀 1,597評(píng)論 0 3
  • 1. ANR異常 Application No Response:應(yīng)用程序無響應(yīng)。在主線程中,是不允許執(zhí)行耗時(shí)的操...
    JackChen1024閱讀 1,598評(píng)論 0 3
  • 文章獨(dú)家授權(quán)公眾號(hào):碼個(gè)蛋更多分享:http://www.cherylgood.cn 談到Android開發(fā),就離...
    Angels_安杰閱讀 1,726評(píng)論 0 3

友情鏈接更多精彩內(nèi)容