view.post源碼解析 (API29)


View.post(runnable) runnable在主線程執行,且能拿到View的寬高等測量繪製信息

view.post投遞消息

  • 如果attachInfo不爲空,則直接由attachInfo的handler來執行runnable 信息
  • 否則,放入HandlerActionQueue中,等待attachInfo賦值,然後由attachInfo.handler執行runnable

綜上,view.post(runnable)都是通過attachInfo.handler發送消息,由handler分發處理。
attachInfo是在dispatchAttachedToWindow中賦值,在dispatchDetachedFromWindow中移除的。

//View.java
	//投遞消息
	public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) { //attachInfo已經賦值,則直接通過attachInfo.handler執行
            return attachInfo.mHandler.post(action);
        }
        getRunQueue().post(action); //否則放入HandlerActionQueue中,等待時機處理
        return true;
    }

	//取消執行runnable對象,和post對應
	public boolean removeCallbacks(Runnable action) {
        if (action != null) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                attachInfo.mHandler.removeCallbacks(action);
                attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
                        Choreographer.CALLBACK_ANIMATION, action, null);
            }
            getRunQueue().removeCallbacks(action);
        }
        return true;
    }
    
	AttachInfo mAttachInfo;
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;

		// Transfer all pending runnables.
        if (mRunQueue != null) { //attachInfo.handler執行之前存儲的runnable信息
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
    }

    void dispatchDetachedFromWindow() {  
        mAttachInfo = null;
    }

private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

HandlerActionQueue管理attachInfo未實例化之前的runnable對象

  • 將runnable 和 delayTime 包裝成一個HandlerAction對象
  • 存儲管理HandlerAction對象,然後通過傳來的handler發消息執行runnable對象
public class HandlerActionQueue {
    private HandlerAction[] mActions;
    private int mCount; //集合容量

    public void post(Runnable action) {
        postDelayed(action, 0);
    }

    public void postDelayed(Runnable action, long delayMillis) {
    //將runnable 和 delayTime 包裝成一個HandlerAction對象
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
        synchronized (this) {
            if (mActions == null) { //默認初始化數組對象容量爲4,
                mActions = new HandlerAction[4];
            }
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

	//更新集合數據mActions
    public void removeCallbacks(Runnable action) {
        synchronized (this) {
            final int count = mCount;
            int j = 0; //j是新的,處理過的的Hnadler集合索引
            final HandlerAction[] actions = mActions;
            for (int i = 0; i < count; i++) { //i是實際的Hnadler集合索引
                if (actions[i].matches(action)) {
                    continue;
                }
                if (j != i) { //不匹配,用後面的覆蓋前面的(j肯定小於等於i)
                    actions[j] = actions[i];
                }
                j++;
            }
            mCount = j; //集合的容量更新
            for (; j < count; j++) { //集合被清除的數據賦爲null
                actions[j] = null;
            }
        }
    }

	//通過handler發送message的形式,在handler所在的線程執行所有action
    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;
        }
    }

    private static class HandlerAction {
        final Runnable action;
        final long delay;

        public HandlerAction(Runnable action, long delay) {
            this.action = action;
            this.delay = delay;
        }

        public boolean matches(Runnable otherAction) {
            return otherAction == null && action == null
                    || action != null && action.equals(otherAction);
        }
    }
}

attachInfo的實例化和賦值

  • attachInfo是在ViewRootImpl的構造函數中實例化的,mHandler的looper是當前線程(主線程)的looper對象,handler也是在主線程中處理消息的

  • ViewRootImple是在onResume之後,在WindowManagerGlobal中實例化的,並通過setView(view, display)綁定了decorView,viewRootImpl是decorView的parentView

  • setView中通過requestLayout發起佈局繪製請求,在下一個16.66ms到來時,執行performTraversals()。

  • 在performMeasure之前,通過host.dispatchAttachedToWindow(attachInfo),遍歷decorView,將attachInfo綁定到每一個View/ViewGroup身上,才能通過attachInfo.handler發消息執行所有runnable對象

  • runnable對象是在performTraversals()執行完畢後,handler纔會繼續從messageQueue中取message,在主線程中執行,所以view.post(runnable)能拿到View的寬高等測量繪製信息。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

final View.AttachInfo mAttachInfo;
final ViewRootHandler mHandler = new ViewRootHandler();

//ViewRootImpl實例化時,attachInfo也實例化
public ViewRootImpl(Context context, Display display) {
        mFirst = true; // true for the first time the view is added
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context);
    }

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout(); //發起佈局繪製請求
				view.assignParent(this);
        }
    }

 private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        if (host == null || !mAdded)
            return;
        if (mFirst) {
            host.dispatchAttachedToWindow(mAttachInfo, 0); //分發attachInfo給每一個view、ViewGroup
        } 
            int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
            int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
            // Ask host how big it wants to be
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            performLayout(lp, mWidth, mHeight);
            performDraw();

    }
}

decorView分發attachInfo給每一個View/ViewGroup

//ViewGroup.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        super.dispatchAttachedToWindow(info, visibility);
      
        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()));
        }
    }

總結:

  1. View.post(Runnable) 內部會自動分兩種情況處理,當 View 還沒 attachedToWindow 時,會先將這些 Runnable 操作緩存下來;否則就直接通過 mAttachInfo.mHandler 將這些 Runnable 操作 post 到主線程的 MessageQueue 中等待執行。
  2. 如果 View.post(Runnable) 的 Runnable 操作被緩存下來了,那麼這些操作將會在 dispatchAttachedToWindow() 被回調時,通過 mAttachInfo.mHandler.post() 發送到主線程的 MessageQueue 中等待執行。
  3. mAttachInfo 是 ViewRootImpl 的成員變量,在構造函數中初始化,Activity View 樹裏所有的子 View 中的 mAttachInfo 都是 ViewRootImpl.mAttachInfo 的引用。
  4. mAttachInfo.mHandler 也是 ViewRootImpl 中的成員變量,在聲明時就初始化了,所以這個 mHandler 綁定的是主線程的 Looper,所以 View.post() 的操作都會發送到主線程中執行,那麼也就支持 UI 操作了。
  5. dispatchAttachedToWindow() 被調用的時機是在 ViewRootImol 的 performTraversals() 中,該方法會進行 View 樹的測量、佈局、繪製三大流程的操作。
  6. Handler 消息機制通常情況下是一個 Message 執行完後纔去取下一個 Message 來執行(異步 Message 還沒接觸),所以 View.post(Runnable) 中的 Runnable 操作肯定會在 performMeaure() 之後才執行,所以此時可以獲取到 View 的寬高。

參考:
【Android源碼解析】View.post()到底幹了啥

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