欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

掌握Android Handler消息機(jī)制核心代碼

 更新時(shí)間:2021年09月27日 10:21:56   作者:白瑞德  
該文主要是分析Handler消息機(jī)制的關(guān)鍵源碼,文章會(huì)從對(duì)handler有一些基本的認(rèn)識(shí)開始介紹,內(nèi)容詳細(xì),感興趣的小伙伴可以參考下

閱讀前需要對(duì)handler有一些基本的認(rèn)識(shí)。這里先簡(jiǎn)要概述一下:

一、handler基本認(rèn)識(shí)

1、基本組成

完整的消息處理機(jī)制包含四個(gè)要素:

  • Message(消息):信息的載體
  • MessageQueue(消息隊(duì)列):用來(lái)存儲(chǔ)消息的隊(duì)列
  • Looper(消息循環(huán)):負(fù)責(zé)檢查消息隊(duì)列中是否有消息,并負(fù)責(zé)取出消息
  • Handler(發(fā)送和處理消息):把消息加入消息隊(duì)列中,并負(fù)責(zé)分發(fā)和處理消息

2、基本使用方法

Handler的簡(jiǎn)單用法如下:

Handler handler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {                     
        super.handleMessage(msg);
    }
};
Message message = new Message();
handler.sendMessage(message);

注意在非主線程中的要調(diào)用Looper.prepare() Looper.loop()方法

3、工作流程

其工作流程如下圖所示:

工作流程 從發(fā)送消息到接收消息的流程概括如下:

  • 發(fā)送消息
  • 消息進(jìn)入消息隊(duì)列
  • 從消息隊(duì)列里取出消息
  • 消息的處理

下面就一折四個(gè)步驟分析一下相關(guān)源碼:

二、發(fā)送消息

handle有兩類發(fā)送消息的方法,它們?cè)诒举|(zhì)上并沒有什么區(qū)別:

sendXxxx()

  • boolean sendMessage(Message msg)
  • boolean sendEmptyMessage(int what)
  • boolean sendEmptyMessageDelayed(int what, long delayMillis)
  • boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
  • boolean sendMessageDelayed(Message msg, long delayMillis)
  • boolean sendMessageAtTime(Message msg, long uptimeMillis)
  • boolean sendMessageAtFrontOfQueue(Message msg)

postXxxx()

  • boolean post(Runnable r)
  • boolean postAtFrontOfQueue(Runnable r)
  • boolean postAtTime(Runnable r, long uptimeMillis)
  • boolean postAtTime(Runnable r, Object token, long uptimeMillis)
  • boolean postDelayed(Runnable r, long delayMillis)
  • boolean postDelayed(Runnable r, Object token, long delayMillis)

這里不分析具體的方法特性,它們最終都是通過調(diào)用sendMessageAtTime()或者sendMessageAtFrontOfQueue實(shí)現(xiàn)消息入隊(duì)的操作,唯一的區(qū)別就是post系列方法在消息發(fā)送前調(diào)用了getPostMessage方法:

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

需要注意的是:sendMessageAtTime()再被其他sendXxx調(diào)用時(shí),典型用法為:

sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

若調(diào)用者沒有指定延遲時(shí)間,則消息的執(zhí)行時(shí)間即為當(dāng)前時(shí)間,也就是立即執(zhí)行。Handler所暴露的方法都遵循這種操作,除非特別指定,msg消息執(zhí)行時(shí)間就為:當(dāng)前時(shí)間加上延遲時(shí)間,本質(zhì)上是個(gè)時(shí)間戳。當(dāng)然,你也可以任意指定時(shí)間,這個(gè)時(shí)間稍后的消息插入中會(huì)用到。 代碼很簡(jiǎn)單,就是講調(diào)用者傳遞過來(lái)的Runnable回調(diào)賦值給message(用處在消息處理中講)。 sendMessageAtTime()sendMessageAtFrontOfQueue方法都會(huì)通過enqueueMessage方法實(shí)現(xiàn)消息的入棧:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

代碼很簡(jiǎn)單,主要有以下操作:

  • message持有發(fā)送它的Handler的引用(這也是處理消息時(shí)能找到對(duì)應(yīng)handler的關(guān)鍵)
  • 設(shè)置消息是否為異步消息(異步消息無(wú)須排隊(duì),通過同步屏障,插隊(duì)執(zhí)行)
  • 調(diào)用MessageQueueenqueueMessage方法將消息加入隊(duì)列

三、消息進(jìn)入消息隊(duì)列

1、入隊(duì)前的準(zhǔn)備工作

enqueueMessage方法是消息加入到MessageQueue的關(guān)鍵,下面分段來(lái)分析一下:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
     throw new IllegalArgumentException("Message must have a target.");
    }
    //...省略下文代碼
}    

這端代碼很簡(jiǎn)單:判斷messagetarget是否為空,為空則拋出異常。其中,target就是上文Handler.enqueueMessage里提到到Handler引用。 接下來(lái)下來(lái)開始判斷和處理消息

boolean enqueueMessage(Message msg, long when) {
    //...省略上文代碼
    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        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();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        //...省略下文代碼
    }
    //...省略下文代碼
}

首先加一個(gè)同步鎖,接下來(lái)所有的操作都在synchronized代碼塊里運(yùn)行 然后兩個(gè)if語(yǔ)句用來(lái)處理兩個(gè)異常情況:

  • 判斷當(dāng)前msg是否已經(jīng)被使用,若被使用,則排除異常;
  • 判斷消息隊(duì)列(MessageQueue)是否正在關(guān)閉,如果是,則回收消息,返回入隊(duì)失?。╢alse)給調(diào)用者,并打印相關(guān)日志

若一切正常,通過markInUse標(biāo)記消息正在使用(對(duì)應(yīng)第一個(gè)if的異常),然后設(shè)置消息發(fā)送的時(shí)間(機(jī)器系統(tǒng)時(shí)間)。 接下來(lái)開始執(zhí)行插入的相關(guān)操作

2、將消息加入隊(duì)列

繼續(xù)看enqueueMessage的代碼實(shí)現(xiàn)

boolean enqueueMessage(Message msg, long when) {
    //...省略上文代碼
    synchronized (this) {
            //...省略上文代碼
            //步驟1
            Message p = mMessages;
            boolean needWake;
            //步驟2
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                //步驟3
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;
                prev.next = msg;
            }
            
            if (needWake) {
                nativeWake(mPtr);
            }
    }
    //步驟4
    return true;
}

首先說(shuō)明MessageQueue使用一個(gè)單向鏈表維持著消息隊(duì)列的,遵循先進(jìn)先出的軟解。 分析上面這端代碼:

第一步:mMessages就是表頭,首先取出鏈表頭部。

第二步:一個(gè)判斷語(yǔ)句,滿足三種條件則直接將msg作為表頭:

  1. 若表頭為空,說(shuō)明隊(duì)列內(nèi)沒有任何消息,msg直接作為鏈表頭部;
  2. when == 0 說(shuō)明消息要立即執(zhí)行(例如 sendMessageAtFrontOfQueue方法,但一般的發(fā)送的消息除非特別指定都是發(fā)送時(shí)的時(shí)間加上延遲時(shí)間),msg插入作為鏈表頭部;
  3. when < p.when,說(shuō)明要插入的消息執(zhí)行時(shí)間早于表頭,msg插入作為鏈表頭部。

第三步:通過循環(huán)不斷的比對(duì)隊(duì)列中消息的執(zhí)行時(shí)間和插入消息的執(zhí)行時(shí)間,遵循時(shí)間戳小的在前原則,將消息插入和合適的位置。

第四步:返回給調(diào)用者消息插入完成。

需要注意代碼中的needWakenativeWake,它們是用來(lái)喚醒當(dāng)前線程的。因?yàn)樵谙⑷〕龆?,?dāng)前線程會(huì)根據(jù)消息隊(duì)列的狀態(tài)進(jìn)入阻塞狀態(tài),在插入時(shí)也要根據(jù)情況判斷是否需要喚醒。

接下來(lái)就是從消息隊(duì)列中取出消息了

四、從消息隊(duì)列里取出消息

依舊是先看看準(zhǔn)備準(zhǔn)備工作

1、準(zhǔn)備工作

在非主線程中使用Handler,必須要做兩件事

  • Looper.prepare() :創(chuàng)建一個(gè)Loop
  • Looper.loop() :開啟循環(huán)

我們先不管它的創(chuàng)建,直接分段看啊循環(huán)開始的代碼:首先是一些檢查和判斷工作,具體細(xì)節(jié)在代碼中已注釋

 public static void loop() {
   //獲取loop對(duì)象
        final Looper me = myLooper();
        if (me == null) {
         //若loop為空,則拋出異常終止操作
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
         //loop循環(huán)重復(fù)開啟
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }
        //標(biāo)記當(dāng)前l(fā)oop已經(jīng)開啟
        me.mInLoop = true;
        //獲取消息隊(duì)列
        final MessageQueue queue = me.mQueue;
        //確保權(quán)限檢查基于本地進(jìn)程,
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);
        boolean slowDeliveryDetected = false;
        //...省略下文代碼
}        

2、loop中的操作

接下來(lái)就是循環(huán)的正式開啟,精簡(jiǎn)關(guān)鍵代碼:

 public static void loop() {
   //...省略上文代碼
   for (;;) {
    //步驟一
            Message msg = queue.next();
            if (msg == null) {
                //步驟二
                return;
            }
           //...省略非核心代碼
            try {
             //步驟三
                msg.target.dispatchMessage(msg);
                //...
            } catch (Exception exception) {
                //...省略非核心代碼
            } finally {
                //...省略非核心代碼
            }
            //步驟四
            msg.recycleUnchecked();
        }
}        

分步驟分析上述代碼:

步驟一:從消息隊(duì)列MessageQueue中取出消息(queue.next()可能會(huì)造成阻塞,下文會(huì)講到)
步驟二:如果消息為null,則結(jié)束循環(huán)(消息隊(duì)列中沒有消息并不會(huì)返回null,而是在隊(duì)列關(guān)閉才會(huì)返回null,下文會(huì)講到)
步驟三:拿到消息后開始消息的分發(fā)
步驟四:回收已經(jīng)分發(fā)了的消息,然后開始新一輪的循環(huán)取數(shù)據(jù)

2.1 MessageQueue的next方法

我們先只看第一步消息的取出,其他的在稍后小節(jié)再看,queue.next()代碼較多,依舊分段來(lái)看

Message next() {
 //步驟一
 final long ptr = mPtr;
    if (ptr == 0) {
  return null;
 }
 //步驟二
 int pendingIdleHandlerCount = -1; 
 //步驟三
 int nextPollTimeoutMillis = 0;
 //...省略下文代碼
} 

第一步:如果消息循環(huán)已經(jīng)退出并且已經(jīng)disposed之后,直接返回null,對(duì)應(yīng)上文中Loop通過queue.next()取消息拿到null后退出循環(huán)
第二部:初始化IdleHandler計(jì)數(shù)器
第三部:初始化native需要用的判斷條件,初始值為0,大于0表示還有消息等待處理(延時(shí)消息未到執(zhí)行時(shí)間),-1則表示沒有消息了。

繼續(xù)分析代碼:

Message next() {
 //...省略上文代碼
 for(;;){
  if (nextPollTimeoutMillis != 0) {
   Binder.flushPendingCommands();
  }
  nativePollOnce(ptr, nextPollTimeoutMillis);
  //...省略下文代碼  
 }
} 

這一段比較簡(jiǎn)單:

開啟一個(gè)無(wú)限循環(huán)
nextPollTimeoutMillis != 0表示消息隊(duì)列里沒有消息或者所有消息都沒到執(zhí)行時(shí)間,調(diào)用nativeBinder.flushPendingCommands()方法,在進(jìn)入阻塞之前跟內(nèi)核線程發(fā)送消息,以便內(nèi)核合理調(diào)度分配資源
再次調(diào)用native方法,根據(jù)nextPollTimeoutMillis判斷,當(dāng)為-1時(shí),阻塞當(dāng)前線程(在新消息入隊(duì)時(shí)會(huì)重新進(jìn)入可運(yùn)行狀態(tài)),當(dāng)大于0時(shí),說(shuō)明有延時(shí)消息,nextPollTimeoutMillis會(huì)作為一個(gè)阻塞時(shí)間,也就是消息在多就后要執(zhí)行。

繼續(xù)看代碼:

Message next() {
 //...省略上文代碼
 for(;;){
  //...省略上文代碼
       //開啟同步鎖
  synchronized (this) {
   final long now = SystemClock.uptimeMillis();     
                //步驟一
                Message prevMsg = null;
                Message msg = mMessages;
                //步驟二
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //步驟三
                if (msg != null) {
                    //步驟四
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //步驟五
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //步驟六
                    nextPollTimeoutMillis = -1;
                }
  } 
  //...省略下文IdleHandler相關(guān)代碼  
 }
} 

分析一下代碼:

第一步:獲取到隊(duì)列頭部
第二步:判斷當(dāng)前消息是否為同步消息(異步消息的target為null),開啟循環(huán)直到發(fā)現(xiàn)同步消息為止
第三步:判斷消息是否為null,不為空?qǐng)?zhí)行第四步,為空?qǐng)?zhí)行第六步;
第四步:判斷消息執(zhí)行的時(shí)間,如果大于當(dāng)前時(shí)間,給前文提到的nextPollTimeoutMillis賦新值(當(dāng)前時(shí)間和消息執(zhí)行時(shí)間的時(shí)間差),在這一步基本完成了本次循環(huán)所有的取消息操作,如果當(dāng)前消息沒有到達(dá)執(zhí)行時(shí)間,本次循環(huán)結(jié)束,新循環(huán)開始,就會(huì)使用上文中提到的nativePollOnce(ptr, nextPollTimeoutMillis) ;方法進(jìn)入阻塞狀態(tài)
第五步:從消息隊(duì)列中取出需要立即執(zhí)行的消息,結(jié)束整個(gè)循環(huán)并返回。
第六部:消息隊(duì)列中沒有消息,標(biāo)記nextPollTimeoutMillis,以便下一循環(huán)進(jìn)入阻塞狀態(tài)
剩下的代碼就基本上是IdleHandler的處理和執(zhí)行了,在IdleHandler小節(jié)里進(jìn)行講解,這里就不展開說(shuō)明了。

五、消息的處理

還記得上文中l(wèi)oop方法中的msg.target.dispatchMessage(msg) ;嗎? 消息就是通過dispatchMessage方法進(jìn)行分發(fā)的。其中target是msg所持有的發(fā)送它的handler的引用,它在發(fā)送消息時(shí)被賦值。 dispatchMessage的源碼如下:

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

代碼很簡(jiǎn)單,通過判斷Message是否有Runable來(lái)決定是調(diào)用callback還是調(diào)用handleMessage方法,交給你定義的Handler去處理。需要注意的是,callback雖然是一個(gè)Runable,但是它并沒有調(diào)用run方法,而是直接執(zhí)行。這說(shuō)明它并沒有開啟新的線程,就是作為一個(gè)方法使用(如果一開始Handler使用kotlin寫的話,此處或許就是一個(gè)高階函數(shù)了)。

六、其他關(guān)鍵點(diǎn)

上面講完了消息處理的主流程,接下來(lái)講一下主流程之外的關(guān)鍵點(diǎn)源碼

1、 Loop的創(chuàng)建

還記得上文中的說(shuō)到的在非主線程中的要調(diào)用Looper.prepare()Looper.loop()方法嗎?這兩個(gè)方法可以理解為初始化Loop和開啟loop循環(huán),而主線程中無(wú)需這么做是因?yàn)樵赼pp啟動(dòng)的main方法中,framework層已經(jīng)幫我們做了。我們分別來(lái)看這兩個(gè)方法:

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

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));
    }
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }    

這里首先使用了一個(gè)靜態(tài)的ThreadLocal確保Loop的唯一性,同時(shí)做到線程隔離,使得一個(gè)線程有且只有一個(gè)Loop實(shí)例。接著初始化Loop,同時(shí)創(chuàng)建MessageQueue (quitAllowed設(shè)置是否允許退出)。在這一步實(shí)現(xiàn)了Loop和消息隊(duì)列的關(guān)聯(lián)。

需要注意的是,Loop的構(gòu)造方式是私有的,我們只能通過prepare 區(qū)創(chuàng)建,然后通過myLooper方法去獲取。

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

ThreadLocal.get源碼:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }    

可以看到,每個(gè)Thread都持有一個(gè)ThreadLocalMap ,它和HashMap使用相同的數(shù)據(jù)結(jié)構(gòu),使用ThreadLocal作為key值,value就是Loop實(shí)例。不難發(fā)現(xiàn):我們只能獲取到當(dāng)前線程的Loop實(shí)例。

Loop也提供了主線程中初始化的辦法prepareMainLooper ,但是這個(gè)方法明確說(shuō)明不允許調(diào)用,只能由系統(tǒng)自己調(diào)用。 這基本上就是Loop創(chuàng)建的關(guān)鍵了,也是在這里完成了Loop和消息隊(duì)列以及線成之間的關(guān)聯(lián)。

2、Handler的創(chuàng)建

Handler的構(gòu)造函數(shù)有以下幾個(gè):

  • public Handler()
  • public Handler(Callback callback)
  • public Handler(Looper looper)
  • public Handler(Looper looper, Callback callback)
  • public Handler(boolean async)
  • public Handler(Callback callback, boolean async)
  • public Handler(Looper looper, Callback callback, boolean async)

其中第一個(gè)和第二個(gè)已經(jīng)被廢棄了,實(shí)際上第1~5個(gè)構(gòu)造方法都是通過調(diào)用public Handler(Callback callback, boolean async)或public Handler(Looper looper, Callback callback, boolean async)實(shí)現(xiàn)的,它們的源碼如下:

 public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
public Handler(@Nullable 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) {
         //注意這個(gè)異常,loop不能為空的,首先要Looper.prepare();
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

兩個(gè)方法最大的區(qū)別就是一個(gè)使用傳遞過來(lái)的loop,一個(gè)直接使用當(dāng)前線程的loop,然后就是相同的一些初始化操作了。這里就出現(xiàn)了一個(gè)關(guān)鍵點(diǎn)handler處理消息所處的線程和創(chuàng)建它的線程無(wú)關(guān),而是和創(chuàng)建它時(shí)loop的線程有關(guān)的。

這也是Handler能實(shí)現(xiàn)線程切換的原因所在: handler的執(zhí)行跟創(chuàng)建handler的線程無(wú)關(guān),跟創(chuàng)建looper的線程相關(guān),假如在子線程中創(chuàng)建一個(gè)Handler,但是Handler相關(guān)的Looper是主線程的,那么如果handler執(zhí)行post一個(gè)runnable,或者sendMessage,最終的handle Message都是在主線程中執(zhí)行的。

3、Message的創(chuàng)建、回收和復(fù)用機(jī)制

我們可以直接使用new關(guān)鍵字去創(chuàng)建一個(gè)Message:

Message message = new Message(); 但是這種做法并不被推薦,官方提供了以下幾個(gè)方法供用戶創(chuàng)建message

這些方法除了形參有些區(qū)別,用來(lái)給message不同的成員變量賦值之外,本質(zhì)上都是通過 obtain()來(lái)創(chuàng)建Message:

public static final Object sPoolSync = new Object();
Message next;
private static Message sPool;

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

這端代碼很簡(jiǎn)單,Message內(nèi)部維持了一個(gè)單線鏈表,使用sPool作為頭部,用來(lái)存儲(chǔ)Message實(shí)體。可以發(fā)現(xiàn),每次調(diào)用者需要一個(gè)新的消息的時(shí)候,都會(huì)先從鏈表的頭部去取,有消息就直接返回。沒有消息才會(huì)創(chuàng)建一個(gè)新的消息。

那么這個(gè)鏈表是在何時(shí)插入消息的呢?接下來(lái)看Message的回收:

public static final Object sPoolSync = new Object();
private static final int MAX_POOL_SIZE = 50;

void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

該方法在每次消息從MessageQueue 隊(duì)列取出分發(fā)時(shí)都會(huì)被調(diào)用,就是在上文提到的Loop.loop()方法里。 代碼也很簡(jiǎn)單,首先將Message的成員變量還原到初始狀態(tài),然后采用頭插法將回收后的消息插入到鏈表之中(限制了最大容量為50)。而且插入和取出的操作,都是使用的同一把鎖,保證了安全性。

注意插入和取出都是對(duì)鏈表的頭部操作,這里和消息隊(duì)列里就不太一樣了。雖然都是使用單向鏈表,回收時(shí)使用頭插和頭取,先進(jìn)后出,是個(gè)棧。而在MessageQueue里是個(gè)隊(duì)列,遵循先進(jìn)先出的原則,而且插入的時(shí)候是根據(jù)消息的狀態(tài)確定位置,并沒有固定的插入節(jié)點(diǎn)。

這是一個(gè)典型的享元模式,最大的特點(diǎn)就是復(fù)用對(duì)象,避免重復(fù)創(chuàng)建導(dǎo)致的內(nèi)存浪費(fèi)。這也是為什么android官方推薦使用這種方式創(chuàng)建消息的原因:就是為了提高效率減少性能開銷。

4、 IdleHandler

IdleHandler 的定義很簡(jiǎn)單,就是一個(gè)定義在MessageQueue里的接口:

  public static interface IdleHandler {
        boolean queueIdle();
    }

根據(jù)官方的解釋,在 Looper循環(huán)的過程中,每當(dāng)消息隊(duì)列出現(xiàn)空閑:沒有消息或者沒到任何消息的執(zhí)行時(shí)間需要滯后執(zhí)行的時(shí)候,queueIdle 方法就會(huì)被執(zhí)行,而其返回的布爾值標(biāo)識(shí)IdleHandler 是永久的還是一次性的:

  • ture:永久的,一旦空閑,就會(huì)執(zhí)行
  • false:一次性的,只有第一次空閑時(shí)會(huì)執(zhí)行

它的使用方法如下:

Looper.getMainLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                return true;
            }
        });

看一下addIdleHandler 的實(shí)現(xiàn)

private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

 public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

代碼很簡(jiǎn)單,就是一個(gè)List來(lái)保存接口的實(shí)現(xiàn)。那么它是怎么實(shí)現(xiàn)在出現(xiàn)空閑時(shí)調(diào)用呢?

還記得在上文MessageQueuenext方法中省略的代碼嗎?

Message next() {
        
        //...省略不相關(guān)代碼

        //步驟一
        int pendingIdleHandlerCount = -1; // -1 只存在第一次迭代中
        for (;;) {
                //...省略不相關(guān)代碼

                //步驟二
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }

                //步驟三
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                //步驟四
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

        
            //步驟五
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;

                //步驟六
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                //步驟七
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //步驟八
            pendingIdleHandlerCount = 0;
        }
    }

接下來(lái)分步驟分析一下代碼:

第一步:在取消息的循環(huán)開始前創(chuàng)建局部變量pendingIdleHandlerCount用來(lái)記錄IdleHandler的數(shù)量,只在循環(huán)開始時(shí)為-1;
第二步:當(dāng)沒有取到Message消息(沒有消息或者沒有可立即執(zhí)行的消息,也沒有進(jìn)去阻塞狀態(tài))或者消息需要延后執(zhí)行,為pendingIdleHandlerCount 賦值記錄IdleHandler的數(shù)量;
第三步:判斷IdleHandler的數(shù)量,如果沒有IdleHandler,則直接結(jié)束當(dāng)前循環(huán),并標(biāo)記循環(huán)可進(jìn)入掛起狀態(tài)。
第四步:判斷是否是第一次,初始化IdleHandler 的List
第五步:開始遍歷所有的IdleHandler
第六步:依次執(zhí)行IdleHandler queueIdle方法
第七部:根據(jù)各IdleHandler queueIdle的返回值判斷IdleHandler 是永久的還是一次性的,將非永久的從數(shù)組里移除;
第八步:修改IdleHandler 的數(shù)量信息pendingIdleHandlerCount ,避免IdleHandler 重復(fù)執(zhí)行。
這就是IdleHandler 的核心原理,它只在消息隊(duì)列為空時(shí),或者消息隊(duì)列的頭部消息為延時(shí)消息時(shí)才會(huì)被觸發(fā)。當(dāng)消息隊(duì)列頭部為延時(shí)消息時(shí),它只會(huì)觸發(fā)一次哦。在前文中取消息的小節(jié)中我們講過:延時(shí)消息在結(jié)束當(dāng)前循環(huán)后進(jìn)入下一路循環(huán)會(huì)觸發(fā)阻塞。

5、Handler在Framework層的應(yīng)用

不知道你有沒有想過為什么Android在主線程里直接幫你調(diào)用了Looper.prepare() Looper.loop()方法,難道只是為了你使用方便嗎?這豈不是有點(diǎn)殺雞用牛刀的感覺?

事實(shí)上遠(yuǎn)沒有這么簡(jiǎn)單,如果你看一下framework的源碼你就會(huì)發(fā)現(xiàn),整個(gè)android app的運(yùn)轉(zhuǎn)都是基于Handler進(jìn)行的。四大組件的運(yùn)行,它們生命周期也是基于Handler事件模型進(jìn)行的,以及點(diǎn)擊事件等。這一切均是由Android系統(tǒng)框架層產(chǎn)生相應(yīng)的message再交由一個(gè)Handler進(jìn)行處理的。這個(gè)Handler就是ActivityThread內(nèi)部類H,貼一段它的代碼截圖

可以看到,四大組件的生命周期甚至內(nèi)存不足,都有handler在參與。

這也解釋了為什么在主線程執(zhí)行耗時(shí)任務(wù)會(huì)導(dǎo)致UI卡頓或者ANR:因?yàn)樗兄骶€程也就是UI線程的邏輯代碼都是在組件的生命周期里執(zhí)行的,而生命周期又受到Handler的事件體系的控制,當(dāng)在任意生命周期做中執(zhí)行耗時(shí)操作,這會(huì)導(dǎo)致消息隊(duì)列MessageQueue中后面的消息無(wú)法得到及時(shí)的處理,就會(huì)造成延遲,直至視覺上的卡頓,嚴(yán)重的則會(huì)進(jìn)一步觸發(fā)ANR的發(fā)生。

到此這篇關(guān)于掌握Android Handler消息機(jī)制核心代碼的文章就介紹到這了,更多相關(guān)Android Handler消息機(jī)制核心代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論