從view.post再看消息處理

從view.post再看消息處理

大家都知道view.post可以在主線程執(zhí)行一段Runnable,并且相比自己定義Handler 而言,更加簡(jiǎn)潔方便。然而這個(gè)方法跟handler.post 有怎樣的區(qū)別。 本文從源碼角度簡(jiǎn)單探究一下兩種方式的實(shí)現(xiàn)細(xì)節(jié)。

view.post 實(shí)現(xiàn)

該方法的實(shí)現(xiàn)非常簡(jiǎn)單

 public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

可以看到第一個(gè)邏輯分支,仍然是通過(guò)handler進(jìn)行消息分發(fā)。 而第二個(gè)分之是通過(guò)一個(gè)RunQueue進(jìn)行的分發(fā)動(dòng)作。

第一個(gè)邏輯分支相對(duì)簡(jiǎn)易,是直接通過(guò)該view 獲取的attachInfo中的Handler進(jìn)行處理。而這個(gè)attachinfo 是在


    void dispatchAttachedToWindow(AttachInfo info, int visibility) 

方法中進(jìn)行賦值的,只有當(dāng)該方法被調(diào)用后,才會(huì)執(zhí)行第一個(gè)邏輯分支。

再看第二個(gè)邏輯分支。

RunQueue 對(duì)應(yīng)的事實(shí)上是 HandlerActionQueue 類的一個(gè)對(duì)象。 HandlerActionQueue 的post方法中會(huì)對(duì)將要發(fā)送的Runnable進(jìn)行緩存,直到executeAction(Handler handler)方法被外部調(diào)用,則會(huì)依次將待處理的Runnable 添加到入?yún)⒌腍andler 消息隊(duì)列中執(zhí)行。

 public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

那么這個(gè)方法是什么時(shí)候被調(diào)用的呢。一共有兩個(gè)地方

  1. 第一是在ViewRootImpl的performTraversal方法中,傳入mAttachInfo的handler執(zhí)行。
  2. 第二是在View 的dispatchAttachedToWindow方法中,也是通過(guò)attachInfo的handler來(lái)執(zhí)行緩存的Runnable 方法

熟悉 ViewRootImpl 的同學(xué)知道,這是整個(gè)視窗的根視圖,并且在每一幀渲染時(shí),它的performTravsal會(huì)被調(diào)用用以整個(gè)頁(yè)面的布局。然而我們實(shí)際使用中,往往是通過(guò)一個(gè)頂層view進(jìn)行post動(dòng)作,因而更多地是出發(fā)第二的方法。

而對(duì)于第二個(gè)方法,我們又和第一個(gè)邏輯的判斷條件分支殊途同歸了。
至此,我們能夠確定的是 dispatchAttachedToWindow 執(zhí)行后,view.post 的Runnable才能夠被執(zhí)行。

dispatchAttachedToWindow

dispatchAttachedToWindow 是Android視圖渲染的關(guān)鍵方法。感興趣的同學(xué)可以查閱相關(guān)資料,這里我們只是簡(jiǎn)述一下它的執(zhí)行機(jī)制。

我們知道視圖的根實(shí)際上是一個(gè)ViewGroup, 它的dispatchAttachedToWindow方法為

    @Override
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
        super.dispatchAttachedToWindow(info, visibility);
        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            final View child = children[i];
            child.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, child.getVisibility()));
        }
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        for (int i = 0; i < transientCount; ++i) {
            View view = mTransientViews.get(i);
            view.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, view.getVisibility()));
        }
    }

即,遍歷所有子view 一次分發(fā)attachToWindow這一個(gè)函數(shù)通知,使得子view一次執(zhí)行。在View框架整個(gè)調(diào)用過(guò)程 ,如果是非葉節(jié)點(diǎn),首先調(diào)用父類的dispatchAttachedToWindow,然后調(diào)用子節(jié)點(diǎn)的dispatchAttachedToWindow;如果是葉節(jié)點(diǎn),則會(huì)調(diào)用View的dispatchAttachedToWindow,整個(gè)調(diào)用過(guò)程可以看成樹(shù)的先序遍歷。而在ViewRootImpl中,只有當(dāng)mFirst == true時(shí),也就是第一次布局時(shí),才會(huì)調(diào)用根的dispathAttachedToWindow方法。也就印證了這個(gè)函數(shù)名,當(dāng)視圖第一次被添加到視窗時(shí)執(zhí)行。

綜上, 調(diào)用view.post方法,如果對(duì)應(yīng)的view 并沒(méi)有被添加到視窗(i.e. 第一次performTraversal 沒(méi)有被執(zhí)行),則該Runnable 會(huì)被緩存至視圖添加完后,再被添加到MainLooper的消息隊(duì)列的對(duì)應(yīng)位置執(zhí)行。 對(duì)比直接使用Handler 進(jìn)行post 的動(dòng)作,更加適合操作一些視圖元素。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 霧霾天,開(kāi)啟了產(chǎn)后上班的匆匆之旅。 從昨天開(kāi)始就進(jìn)入緊張狀態(tài),上午帶娃打疫苗,下午逛街一個(gè)半小時(shí)置辦完上班行頭,約...
    Min_Xu閱讀 110評(píng)論 0 0
  • 夢(mèng)里年光指縫溜,風(fēng)高云淡碧波流。 閑來(lái)石上望長(zhǎng)楸。 縱橫百年身欲老,參禪萬(wàn)事始于休。 鵲枝忽下立青牛。
    婉兮清漾閱讀 667評(píng)論 4 12
  • 在這除夕之夜到來(lái)之際,外面鞭炮聲,伴隨著鳥(niǎo)兒聲從窗外傳來(lái),喜迎新春,真是熱鬧非凡,孩子們一個(gè)在房里看書(shū),一個(gè)...
    霞憶隨行閱讀 268評(píng)論 0 0

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