view.post 是如何能獲取到view的寬高的?

  //View
   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;
    }

  void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        ...
       
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        ...
}

view調(diào)用post時(shí),先會(huì)進(jìn)行 mAttachInfo != null 的判斷,而 mAttachInfo 是在view加載到Window中的時(shí)候才進(jìn)行的賦值。
當(dāng)view還未被加載到window時(shí),會(huì)先將 action 放到 getRunQueue() 中, getRunQueue 就是mRunQueue
當(dāng)view加載到window后,會(huì)從mRunQueue中取出之前的action。

    //HandlerActionQueue
    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;
        }
    }

    //ViewRootImpl
    private void performTraversals() {
        ...
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        ...
         //不會(huì)立即執(zhí)行,只會(huì)將任務(wù)先加到隊(duì)列中,等 performTraversals 執(zhí)行完畢后才輪到它
         getRunQueue().executeActions(mAttachInfo.mHandler);
        ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
    }

這一塊很奇怪,按順序走,dispatchAttachedToWindow 其實(shí)是在 performMeasure 之前被調(diào)用的,這難到是有可能post不一定能獲取到寬高嗎?
進(jìn)到 executeActions 看,發(fā)現(xiàn)里面的實(shí)現(xiàn)其實(shí)是將 action 再次傳入到 mAttachInfo.mHandler 中,根據(jù)
ViewRootImpl 的初始化方法發(fā)現(xiàn) , 此 handler 為:

//ViewRootImpl
    public ViewRootImpl(Context context, Display display) {
        ...
        mChoreographer = Choreographer.getInstance();
        ...
    }

    final ViewRootHandler mHandler = new ViewRootHandler();

    void doTraversal() {
         ...
         performTraversals();
        ...
    }

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    void scheduleTraversals() {
            ...
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...
    }

    //Choreographer
    public static Choreographer getInstance() {
        return sThreadInstance.get();
    }

    private Choreographer(Looper looper, int vsyncSource) {
        ...
        mHandler = new FrameHandler(looper);
        ...
      }

我們回到 performTraversals 的調(diào)用上,可以發(fā)現(xiàn)是由 scheduleTraversals -> mChoreographer -> mTraversalRunnable -> doTraversal 進(jìn)行的調(diào)用。
進(jìn)一步觀察 mChoreographer ,發(fā)現(xiàn)其創(chuàng)建是跟隨著 ViewRootImpl 的創(chuàng)建,其內(nèi)部的 handler 與ViewRootImpl 是相同線程,相同的 loop

再解釋一下:
performTraversals 方法 先執(zhí)行 host.dispatchAttachedToWindow(mAttachInfo, 0);
然后執(zhí)行 getRunQueue().executeActions(mAttachInfo.mHandler);
由于 performTraversals 本身就是在 handler 中運(yùn)行 , executeActions 并不會(huì)立馬執(zhí)行,只是將 message 加到隊(duì)列中。
之后就會(huì)去執(zhí)行 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
等到 measure 執(zhí)行完成后,下一個(gè) message 才是獲取寬高的代碼

那么回到handler的消息機(jī)制,action 是在消息隊(duì)列中循環(huán)調(diào)用,需要上一次執(zhí)行完才能執(zhí)行下一個(gè),那么首先會(huì)執(zhí)行完 TraversalRunnable 也就是說 Measure Layout 會(huì)先運(yùn)行完,再去運(yùn)行我們加到handler里的代碼,從而就保證了view.post 能獲取到寬高。

參考博客

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

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