分析源碼找出爲何onCreate() 和 onResume() 中獲取不到View的寬高值?

首先,提出一個問題,下面三處打印輸出的結果是什麼呢?
帶着問題思考一下,然後猜測一下輸出結果,之後我們再帶着問題去探尋源碼;

public class MyActivity extends Activity {

    private static final String TAG = MyActivity.class.getSimpleName();
    private Button mButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton = findViewById(R.id.my_button);
        // 打印輸出日誌 1
        Log.e(TAG, "打印輸出日誌 1---> onCreate() 獲取View的測量寬度:" + mButton.getWidth());
		 // 打印輸出日誌 2
        mButton.post(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "打印輸出日誌 2---> onCreate() 中 通過post方法獲取View的測量寬度:" + mButton.getWidth());
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
         // 打印輸出日誌 3
        Log.e(TAG, "打印輸出日誌 3---> onResume() 獲取View的測量寬度:" + mButton.getWidth());
    }
}

這裏首先給出一下輸出結果,看看給你的答案一致不?
在這裏插入圖片描述
從圖上的打印輸出,我們可以看到,onCreate() 和 onResume() 中我們是獲取不到View的測量值的,在onCreate() 中通過post方法可以獲取到View的測量值;

如果心中仍疑惑不解的,那麼我們就來帶着問題探尋源碼吧…
在Activity的 onCreate() 方法中,我們通過 setContentView(R.layout.activity_view_dispatch) 指定了頁面的佈局,之後我們就可以從我們設置的佈局中獲取到指定的View。爲何我們已經指定了佈局,也可以查找到指定的View,確不能獲取到指定View的測量值呢?

我們來看一下 Activity#setContentView(@LayoutRes int layoutResID) 都做了些什麼?

跟進去查看一下
Activity#setContentView(@LayoutRes int layoutResID):

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

getWindow() 方法返回的是一個抽象類 Window,而在Android源碼中的唯一實現是 PhoneWindow,找到 PhoneWindow 查看
PhoneWindow#setContentView(int layoutResID):

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
        	// 初始化 DecorView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
		......省略代碼
    }

PhoneWindow#installDecor():

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
        	// 註釋 1
            mDecor = generateDecor(-1);
            ......省略代碼
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
        	// 註釋 2
            mContentParent = generateLayout(mDecor);
			......省略代碼
            if (decorContentParent != null) {
				......省略代碼--DecorView的父容器配置icon等
            } else {
                ......省略代碼--根據代碼設置,是否展示Title
            }
			......省略代碼--設置一些轉場動畫等屬性
        }
    }

註釋 1: 初次加載窗口時,mDecor 爲空,則調用 generateDecor(-1) 方法生成一個 DecorView 對象並返回。

PhoneWindow#generateDecor(int featureId):

protected DecorView generateDecor(int featureId) {
        ......省略代碼--判斷系統上下文環境Context
        return new DecorView(context, featureId, this, getAttributes());
    }

該方法很短,就是生成一個 DecorView 對象返回,DecorView extends FrameLayout 不同版本的源碼有稍微的區別,低版本 DecorView 是PhoneWindow的內部類,高版本是一個單獨的類。

註釋 2: 根據條件設置,初始化要加載到 DecorView 中的佈局,我們來看看源碼
PhoneWindow#generateLayout(DecorView decor):

protected ViewGroup generateLayout(DecorView decor) {
    ......省略代碼
    ......省略代碼--根據各種主題設置默認佈局等
    int layoutResource;
    int features = getLocalFeatures();
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
        setCloseOnSwipeEnabled(true);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                R.attr.dialogTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
            layoutResource = a.getResourceId(
                R.styleable.Window_windowActionBarFullscreenDecorLayout,
                R.layout.screen_action_bar);
        } else {
            layoutResource = R.layout.screen_title;
        }
    } else {
        // 默認佈局樣式,無須任何樣式的裝飾
        layoutResource = R.layout.screen_simple;
    }
     
    mDecor.startChanging();
    // 將佈局解析到 DecorView 中,加載的佈局是一個系統內嵌的佈局
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    // ID_ANDROID_CONTENT 是 android.R.id.content, 這個 View 是從 DecorView 裏面去找的,
    // 也就是從系統的 layoutResource 裏面找一個id是 android.R.id.content 的一個 FrameLayout,
    // 我們在代碼中通過 setContentView 載入的佈局文件就是添加到這個裏面的
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

	......省略代碼
    return contentParent;
}

接下來查看
DecorView#onResourcesLoaded(LayoutInflater inflater, int layoutResource):

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        ......省略代碼
        // 加載系統內嵌的佈局文件
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {
            // Put it below the color views.
            // 將加載的系統佈局添加到當前 DecorView 中
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

該方法的作用就是,將加載的系統內嵌的佈局文件,通過 DecorView#addView() 方法添加到當前的 DecorView 裏面,DecorView 繼承自FrameLayout,所以這裏調到的是父類 ViewGroup#addView() 方法。

ViewGroup#addView(View child, int index, LayoutParams params):

    public void addView(View child, int index, LayoutParams params) {
		......省略代碼
        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        // 此處的 requestLayout 是 View 的方法
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

對於看過源碼的同學,此時是不是該有疑問了?requestLayout() 和 invalidate(true) 方法刷新界面的時候,不會去重新測量,擺放和繪製View的嗎?
結合下圖,這裏我用大白話來解釋一下,平時在開發程序寫界面的時候,遇到需要刷新界面展示的時候,我們可以調用 requestLayout() 和 invalidate() 來進行界面佈局的重新測量、擺放和重繪。只是這個時候測量重繪事件需要通過一層層調用,最終將事件傳遞到最頂層的佈局,也就是ViewRootImpl中,然後由最頂層的佈局一層層的向下傳遞,這也是Android中View的工作原理。這裏不展開說,書上或者很多博客都有講到View工作原理,有興趣的童鞋可以自行查看。
回到剛纔的問題,我們通過 setContentView() 方法,設置佈局,初始化DecorView,然後通過 DecorView 加載所需的系統內嵌佈局,然後再將其由 addView() 加入到 DecorView 中,再刷新 View 的視圖,只是此時我們調用的 requestLayout() 和 invalidate() 是 View 的方法。而我們平時需要測量重繪界面的時候,最終走到的是頂層的 ViewRootImpl 的 requestLayout() 方法,在 setContentView() 方法調用之後,只是初始化了一些佈局,ViewRootImpl 還沒有被創建出來,所以此時走不到 ViewRootImpl 的 requestLayout() 方法,也就不會啓動 View 的一系列測量繪製流程。因此我們自然就獲取不到指定 View 的測量值咯!
在這裏插入圖片描述
那麼 ViewRootImpl 是在哪裏被創建出來的呢?
我們來看一下 Activity 的啓動流程,不詳說,不然有點跑題,博文也太長。
我這裏就當自己做筆記,把之前看的 9.0 系統的啓動流程記錄一下,9.0 系統和之前的代碼不太一樣,我們從 realStartActivityLocked() 這個方法來看,realStartActivityLocked() 之前的流程 9.0 系統之後 和 9.0 系統之前的差不多,從這裏之後,變的不一樣了

先看一下 9.0 以前的源碼
9.0 系統之前 ActivityStackSupervisor#realStartActivityLocked():

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
        try {
			......省略代碼
			// 9.0之前是通過進程間通信的,調用 Activity 的 內部類 ApplicationThread 的 scheduleLaunchActivity
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info,
                    // TODO: Have this take the merged configuration instead of separate global and
                    // override configs.
                    mergedConfiguration.getGlobalConfiguration(),
                    mergedConfiguration.getOverrideConfiguration(), r.compat,
                    r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                    r.persistentState, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profilerInfo);
			......省略代碼
        } catch (RemoteException e) {
       		......省略代碼
        }
		......省略代碼
        return true;
    }

9.0 之前的系統是通過進程間通信的方式,調用 ActivityThread 的內部類 ApplicationThread 的 scheduleLaunchActivity 方法。之後的方法,熟悉源碼的應該都知道吧,發消息,然後由內部類 H 來處理消息,然後走到 handleLaunchActivity() 方法,再走到 performLaunchActivity() 方法等等,不再一一贅述了。

再對比看一下 9.0 之後的源碼
9.0 系統之後 ActivityStackSupervisor#realStartActivityLocked():

  boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {
		......省略代碼
        try {
            try {
           		......省略代碼
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
				......省略代碼
            } catch (RemoteException e) {
           		......省略代碼
            }
        } finally {
            endDeferResume();
        }
		......省略代碼
        return true;
    }

9.0 系統之後,這裏是創建了一個事務,然後進行事物的調度。mService.getLifecycleManager() 方法返回一個 ClientLifecycleManager 對象,接下來我們需要找到這個類對象,然後查看

ClientLifecycleManager#scheduleTransaction(ClientTransaction transaction):

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        // 繼續調用 ClientTransaction 的 schedule() 方法
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }
    }

該方法很短,裏面繼續調用 ClientTransaction 的 schedule() 方法
ClientTransaction#schedule() :

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

這個方法也很簡單,這裏的 mClient 是 IApplicationThread 類型的,我們需要找到他的實現類,也就是 ActivityThread 的內部類 ApplicationThread,查看他的

ActivityThread#ApplicationThread#scheduleTransaction(ClientTransaction transaction):

        @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }

這裏,ActivityThread 中沒有 scheduleTransaction() 方法,我們找到他的父類 ClientTransactionHandler,然後查看

ClientTransactionHandler#scheduleTransaction(ClientTransaction transaction):

    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        // 發送消息到子類 ActivityThread 的 內部類 H 進行處理,H繼承自 Handler
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

ActivityThread#H#handleMessage(Message msg):

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                .......省略代碼
                case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;
        		......省略代碼
            }
           	......省略代碼
        }

Handler消息處理中,調用了
TransactionExecutor#execute(ClientTransaction transaction):

    public void execute(ClientTransaction transaction) {
        ......省略代碼
		// 關鍵代碼 1
        executeCallbacks(transaction);
		// 關鍵代碼 2
        executeLifecycleState(transaction);
        mPendingActions.clear();
        ......省略代碼
    }

execute() 關鍵代碼 1
TransactionExecutor#executeCallbacks(ClientTransaction transaction):

    public void executeCallbacks(ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        ......省略代碼--判空操作,日誌打印

        final IBinder token = transaction.getActivityToken();
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

        final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
        final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState() : UNDEFINED;
        // Index of the last callback that requests some post-execution state.
        final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
        	// 遍歷獲取到 ClientTransactionItem 對象,然後開啓事務
            final ClientTransactionItem item = callbacks.get(i);
            if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
            final int postExecutionState = item.getPostExecutionState();
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                    item.getPostExecutionState());
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState, transaction);
            }
			// 執行 ClientTransactionItem 事務
            item.execute(mTransactionHandler, token, mPendingActions);
            item.postExecute(mTransactionHandler, token, mPendingActions);
            if (r == null) {
                // Launch activity request will create an activity record.
                r = mTransactionHandler.getActivityClient(token);
            }

            if (postExecutionState != UNDEFINED && r != null) {
                // Skip the very last transition and perform it by explicit state request instead.
                final boolean shouldExcludeLastTransition =
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
            }
        }
    }

剛開始看到這裏,我也是一臉懵,這是要幹嘛哈,點進所有能看的,也沒看懂接下來要幹嘛?最後我想看看,這個循環遍歷拿到的 ClientTransactionItem 是幹嘛的?他的 execute() 方法做了什麼?
既然是從列表中 getCallbacks() 取出來的,能取那當然就有存的,我們就來找一下,在哪裏 addCallback() 進去的吧。

前面我們說過,9.0 系統之後 Activity 的啓動流程有了改變,那麼還是回到改變比較大的地方重新找找吧,我們回到
ActivityStackSupervisor#realStartActivityLocked():

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {
		......省略代碼
        try {
			......省略代碼
            try {
				......省略代碼
                // Create activity launch transaction.
                // 創建一個 Activity 的啓動事務
                final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.appToken);

                final DisplayContent dc = r.getDisplay().mDisplayContent;
                // 看到這裏是不是有點小激動了,找到了addCallback,看到了與啓動有點兒關係的類,找到看看吧
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                        r.icicle, r.persistentState, results, newIntents,
                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                r.assistToken));
                // Set desired final state.
                // 看下面的各個類的名字,我們可以猜測這裏是處理和生命週期有關的
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
				......省略代碼
            } catch (RemoteException e) {
            	......省略代碼
            }
        } finally {
            endDeferResume();
        }
		......省略代碼
        return true;
    }

重看 realStartActivityLocked() 方法,9.0 系統之後改成現在的,創建一個 Activity 的啓動事務,並調度事務來進行 Activity 的啓動,我們看一下這個事務是如何構建的?

    public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
        ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
        if (instance == null) {
            instance = new ClientTransaction();
        }
        instance.mClient = client;
        instance.mActivityToken = activityToken;

        return instance;
    }

這裏通過 ClientTransaction.obtain() 方法,獲取到一個 ClientTransaction 對象,方法中可以看到,這裏用到了線程池,應該是到線程池中去獲取一個可重用的事務,具體代碼不跟了,擔心越寫越多了,跑偏咯!

然後就是給這個獲取到事務,賦值一些與啓動和生命週期相關的回調事件,至此,我們找到了事務中與啓動和生命週期有關的回調是如何添加的。

回到 TransactionExecutor#executeCallbacks(ClientTransaction transaction): 方法中,我們知道了循環遍歷拿到的 ClientTransaction 對象是 LaunchActivityItem 類,他實現了抽象類 ClientTransactionItem,並重寫其 execute() 方法。

LaunchActivityItem#execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) :

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client, mAssistToken);
        // client 的實現類是 ActivityThread
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

到這裏,我們終於看到了熟悉的 ActivityThread#handleLaunchActivity() 方法了,但是這裏和 9.0 系統之前的代碼不一樣,之前的代碼是執行 ActivityThread#performLaunchActivity() 方法,然後走到 ActivityThread#handleResumeActivity() 方法。現在 9.0 系統之後的代碼,ActivityThread#handleLaunchActivity() 方法中,執行了 ActivityThread#performLaunchActivity() 方法之後,沒有了 ActivityThread#handleResumeActivity() 方法。

還記得在 ActivityStackSupervisor#realStartActivityLocked() 方法中添加的與 Activity 生命週期有關的回調嗎?ActivityLifecycleItem 類,他是一個抽象類,ResumeActivityItem 和 PauseActivityItem 都繼承自他。加入之後,他在哪裏執行的呢?回到 TransactionExecutor#execute(ClientTransaction transaction): 中的關鍵代碼 2

execute() 關鍵代碼 2
TransactionExecutor#executeLifecycleState(ClientTransaction transaction):

    private void executeLifecycleState(ClientTransaction transaction) {
    	// realStartActivityLocked() 方法中,由於我們是第一次啓動,andResume爲True
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
		......省略代碼--判空操作
        final IBinder token = transaction.getActivityToken();
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
		......省略代碼--日誌打印、判空

        // Cycle to the state right before the final requested state.
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);

        // Execute the final transition with proper parameters.
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }

在 realStartActivityLocked() 方法中,由於我們是第一次啓動,andResume爲True,所以添加到事務中的是 ResumeActivityItem 對象,接着執行其 execute() 方法

ResumeActivityItem#execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions):

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
        // client 的實現類是 ActivityThread,看到了熟悉的方法
        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                "RESUME_ACTIVITY");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

看到了熟悉的方法,跟進查看
ActivityThread#handleResumeActivity():

 public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
		......省略代碼--判空操作

        final Activity a = r.activity;
		......省略代碼--日誌打印
        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
		......省略代碼
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            ......省略代碼
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 執行到 WindowManagerImpl 的 addView()
             		// 然後會跳轉到 WindowManagerGlobal 的 addView()
                    wm.addView(decor, l);
                }
                ......省略代碼
            }
        } 

        // The window is now visible if it has been added, we are not
        // simply finishing, and we are not starting another activity.
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            ......省略代碼--由於第一次啓動,只有添加add操作,暫時用不到更新操作
            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
	......省略代碼
    }

主要看一下 addView() 方法做了什麼?找到實現類 WindowManagerImpl,看一下 addView() 方法

WindowManagerImpl#addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params):

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // 繼續調用 WindowManagerGlobal 的 addView
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

跟進查看
WindowManagerGlobal#addView():

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
		......省略代碼--判空、異常等
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        ......省略代碼

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
			......省略代碼
			// 初始化 ViewRootImpl 的實例
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            // do this last because it fires off messages to start doing things
            try {
            	// 調用 ViewRootImpl#setView 設置 root 佈局
             	// 其中 view 爲傳下來的 DecorView 對象
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

該方法中,先初始化一個 ViewRootImpl 實例,也即是上面圖示的,PhoneWindow的根佈局,那麼,我們是不是找到了 ViewRootImpl 在哪裏實例化的了呢?不容易哈!然後調用 ViewRootImpl#setView() 方法
ViewRootImpl#setView():

    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.
                // 調用 ViewRootImpl 的 requestLayout()
                requestLayout();
                ......省略代碼
            }
        }
    }

ViewRootImpl#requestLayout():

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            // 關鍵代碼,這也是很多博客開始分析View的繪製流程的入口
            scheduleTraversals();
        }
    }

ViewRootImpl#scheduleTraversals():

 @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 通過postSyncBarrier()設置Handler消息的同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // Choreographer 通過 postCallback 提交一個任務,mTraversalRunnable是要執行的回調
            // 有了同步屏障mTraversalRunnable就會被優先執行
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

mTraversalRunnable 是 TraversalRunnable 類的實例, TraversalRunnable 是一個實現了 Runnable 接口的任務回調類,他的 run() 方法,中會調用 ViewRootImpl#doTraversal() 方法,ViewRootImpl#doTraversal() 方法中 會調用 ViewRootImpl#performTraversals()。

Choreographer 通過 postCallback 提交一個任務,mTraversalRunnable是要執行的回調,有了同步屏障mTraversalRunnable就會被優先執行,至於爲何有了同步屏障mTraversalRunnable就會被優先執行?可以查看分析Handler之同步屏障機制與Android的屏幕刷新機制在源碼中的應用

ViewRootImpl#performTraversals():

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        mIsInTraversal = true;
        ......省略代碼
        
        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
            mForceNextWindowRelayout = false;
            
            if (!mStopped || mReportNextDraw) {
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                        updatedConfiguration) {
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                    // Ask host how big it wants to be
                    // 關注方法 1 performMeasure 測量方法
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                    // Implementation of weights from WindowManager.LayoutParams
                    // We just grow the dimensions as needed and re-measure if
                    // needs be
                    int width = host.getMeasuredWidth();
                    int height = host.getMeasuredHeight();
                    boolean measureAgain = false;
					......省略代碼
					// 由measureAgain判斷是否需要再次測量
                    if (measureAgain) {
                    	// 關注方法 1 performMeasure 測量方法
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }
                    layoutRequested = true;
                }
            }
        } else {
            maybeHandleWindowMove(frame);
        }

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
        	// 關注方法 2 performLayout 位置擺放方法
            performLayout(lp, mWidth, mHeight);
            ......省略代碼
        }
        ......省略代碼
        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;

        if (!cancelDraw) {
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).startChangingAnimations();
                }
                mPendingTransitions.clear();
            }
            // 關注方法 3 performDraw 繪製方法
            performDraw();
        } else {
            ......省略代碼
        }
        mIsInTraversal = false;
    }

View 的測繪繪製流程就是從 ViewRootImpl#performTraversals() 開始的,而這個方法的調用是在 onResume() 方法之後,所以在 onCreate() 和 onResume() 方法中拿不到 View 的測量值也就很容易理解了吧。

爲何打印輸出日誌2 可以獲取到 View 的測量值呢?由於篇幅太長了,留待後面再寫吧!

如有不足可以提出,互相交流哈!!!

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