setContentView都作了些什麼事

從Activity開始

Activity代碼很簡單,調用getWindow().setContentView(layoutResID),即調用了PhoneWindow的setContent()方法
貼一下PhoneWindow的setContent方法bash

@Override
public void setContentView(int layoutResID) {
   //安裝DecorView
    if (mContentParent == null) {
        installDecor();
    } 
    // ·······省略部分代碼·······
        //將設置的layoutResID的佈局放入到 mContentParent中  
        mLayoutInflater.inflate(layoutResID, mContentParent);
    // ·······省略部分代碼·······
}
複製代碼

關鍵方法installDecoride

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        //1.構建DecorView 
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        //2.生成mContentParent  mContentParent實際是id爲ID_ANDROID_CONTENT的FrameLayout,即要添加布局的父佈局。
        mContentParent = generateLayout(mDecor);
        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                R.id.decor_content_parent);

         //3.設置Activity標題欄相關
        if (decorContentParent != null) {
            mDecorContentParent = decorContentParent;
            mDecorContentParent.setWindowCallback(getCallback());
            if (mDecorContentParent.getTitle() == null) {
                mDecorContentParent.setWindowTitle(mTitle);
            }

            final int localFeatures = getLocalFeatures();
            for (int i = 0; i < FEATURE_MAX; i++) {
                if ((localFeatures & (1 << i)) != 0) {
                    mDecorContentParent.initFeature(i);
                }
            }

            mDecorContentParent.setUiOptions(mUiOptions);

            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                    (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
                mDecorContentParent.setIcon(mIconRes);
            } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                    mIconRes == 0 && !mDecorContentParent.hasIcon()) {
                mDecorContentParent.setIcon(
                        getContext().getPackageManager().getDefaultActivityIcon());
                mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
            }
            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                    (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
                mDecorContentParent.setLogo(mLogoRes);
            }

            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
                invalidatePanelMenu(FEATURE_ACTION_BAR);
            }
        } else {
            mTitleView = findViewById(R.id.title);
            if (mTitleView != null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    final View titleContainer = findViewById(R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    mContentParent.setForeground(null);
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
        //4.設置背景
        if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
            mDecor.setBackgroundFallback(mBackgroundFallbackResource);
        }

        // 5.獲取過渡元素相關信息
        if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
            // ·······省略部分代碼·······
        }
    }
}
複製代碼

installDecor()主要有四個部分oop

  1. 構建DecorView
  2. 構建Layout並獲取mContentParent對象,要添加的佈局的直接父佈局
  3. 設置Activity標題欄相關
  4. 設置DecorView背景
  5. 獲取過渡動畫信息

至此,DecoverView構建完成,並將佈局添加入DecoverView中佈局

DecoverView何時添加到Window中呢

咱們知道,在Activity生命週期,onStart方法在視圖可見時觸發,那麼DecoverView添加到window確定在onStart方法以前
咱們一步一步往前找會發現
Activity.onStart <- Instrumentation.callActivityOnStart <- Activity.performStart <- Activity.performRestart <- Activity.performResume <- ActivityThread.performResumeActivity <- ActivityThread.handleResumeActivity
咱們從handleResumeActivity分析post

(有興趣的能夠繼續向前查找 Activity.startActivityForResult -> Instrumentation.execStartActivity -> ActivityManagerService.startActivity -> ActivityManagerService.startActivityAsUser -> ActivityStarter.execute -> ActivityStarter.startActivity -> ActivityStarter.startActivityUnchecked-> ActivityStack.ensureActivitiesVisibleLocked -> ActivityStack.makeVisibleAndRestartIfNeeded -> ActivityStackSupervisor.startSpecificActivityLocked -> ActivityStackSupervisor.realStartActivityLocked -> ClientLifecycleManager.scheduleTransaction -> ApplicationThread.scheduleTransaction -> ActivityThread.scheduleTransaction -> Activity.sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction) -> ActivityThread.H.handleMessage case:EXECUTE_TRANSACTION -> TransactionExecutor.execute -> TransactionExecutor.executeLifecycleState -> ResumeActivityItem.execute -> ActivityThread.handleResumeActivity )
動畫

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // 取消空閒狀態的 GC任務
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
    // 1.執行Activity的resume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    // ·······省略部分代碼·······
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        // 2.將decorView設置爲INVISIBLE
        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 (r.mPreserveWindow) {
            a.mWindowAdded = true;
            r.mPreserveWindow = false;
            // Normally the ViewRoot sets up callbacks with the Activity
            // in addView->ViewRootImpl#setView. If we are instead reusing
            // the decor view we have to notify the view root that the
            // callbacks may have changed.
            ViewRootImpl impl = decor.getViewRootImpl();
            if (impl != null) {
                impl.notifyChildRebuilt();
            }
        }
        //3. 將decor添加到WindowManager中
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            } else {
                a.onWindowAttributesChanged(l);
            }
        }
    } else if (!willBeVisible) {
        if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
        r.hideForNow = true;
    }
    
    // ·······省略部分代碼·······
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        // ·······省略部分代碼·······
        r.activity.mVisibleFromServer = true;
        mNumVisibleActivities++;
        if (r.activity.mVisibleFromClient) {
            //4. 設置Activity可見
            r.activity.makeVisible();
        }
    }
    
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    // 添加空閒任務
    Looper.myQueue().addIdleHandler(new Idler());
}

複製代碼

主要包含三部分ui

  1. 執行Activity的onResume方法
  2. 若是r.window==null 將DecorView設置爲INVISIBLE 並將DecorView添加到WindowManager
  3. 設置Activity可見裏面也會調用wm.addView(decorView)

WindowManager.addView到底作了什麼事

wm.addView的方法實如今WindowManagerImpl中,而後又調用了代理類WindowManagerGlobal的addView方法,下面看下WindowManagerGlobal.addView方法this

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        // ·······省略部分代碼·······
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            //1. 添加系統屬性更改回調,發送變化時,調用ViewRootImpl的loadSystemProperties方法
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            
            //2.判斷該view是否已被添加,若是被添加過,而且在正在銷燬的列表中,則進行銷燬,不然拋出非法狀態異常
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
            }

            // ·······省略部分代碼·······
            //3.建立ViewRootImpl對象
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // 4.調用ViewRootImpl的setView方法  
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
複製代碼

主要有四步:spa

  1. 添加系統屬性更改回調,發送變化時,調用ViewRootImpl的loadSystemProperties方法
  2. 判斷該view是否已被添加,若是被添加過,而且在正在銷燬的列表中,則進行銷燬,不然拋出非法狀態異常
  3. 建立ViewRootImpl對象
  4. 調用ViewRootImpl的setView方法
    從源碼能夠看出,最終調用了ViewRootImpl.setView方法

不是View卻實現ViewParent的頂部視圖ViewRootImpl

從ViewParent註釋能夠看出,視圖的頂部,協調View和WindowManager,不少程度上實現了WindowManagerGlobal的細節。
咱們從setView開始看線程

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                //向DisplayManager註冊顯示屏監聽器  能夠監聽到顯示屏的打開和關閉
                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);

                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();

                // 手機反饋事件處理
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);
                if (mWindowAttributes.packageName == null) {
                    mWindowAttributes.packageName = mBasePackageName;
                }
               // ·······省略部分代碼·······
                //建立mSurfaceHolder
                if (view instanceof RootViewSurfaceTaker) {
                    mSurfaceHolderCallback =
                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
                    if (mSurfaceHolderCallback != null) {
                        mSurfaceHolder = new TakenSurfaceHolder();
                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
                        mSurfaceHolder.addCallback(mSurfaceHolderCallback);
                    }
                }

                // ·······省略部分代碼·······
             
                // 1. 在添加到windowManager以前 安排一次佈局  ,以確保咱們在從系統接收任何其餘事件以前進行從新佈局。
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 2.經過IPC將window添加入WindowSeession進行顯示
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } 
                // ·······省略部分代碼·······

                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    // 3. 建立窗口輸入事件接收器,並設置輸入管道流
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
                // 4. 將ViewRootImpl綁定爲DecorView的Parent
                view.assignParent(this);

                // ·······省略部分代碼·······
                // 5. 經過責任鏈模式處理輸入階段 主要針對觸摸事件和物理按鍵事件
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
            }
        }
    }
複製代碼

把代碼主要分爲了五步

  1. requestLayout()在添加到windowManager以前 安排一次佈局,以確保咱們在從系統接收任何其餘事件以前進行從新佈局。
  2. 經過IPC將window添加入WindowSeession進行顯示
  3. 建立窗口輸入事件接收器,並設置輸入管道流
  4. 將ViewRootImpl綁定爲DecorView的Parent
  5. 經過責任鏈模式處理輸入階段 主要針對觸摸事件和物理按鍵事件

看下requestLayout方法

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
複製代碼

代碼很簡單,檢查線程,執行scheduleTraversals 檢查線程會判斷當前線程是否爲UI線程,若是不是UI線程,則拋出異常,這裏就是爲何子線程不能更新UI的緣由。
可是子線程真的不能更新UI嗎?從上面分析咱們能夠看出,當在wm.addView時,纔會建立的ViewRootImpl,而wm.addView,是在onResume時執行,因此若是在onResume執行前,是能夠在子線程更新UI。(其實這個時候視圖並無被添加到view中)

接下來看下scheduleTraversals方法

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 1.在MessageQueue中添加同步柵欄
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 2.由編舞者處理  佈局和繪製 
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            // 3.通知渲染層將有新的幀
            notifyRendererOfFramePending();
            // 若是須要釋放繪製鎖
            pokeDrawLockIfNeeded();
        }
    }

複製代碼
  1. 在MessageQueue中添加同步柵欄
  2. 由編舞者處理 佈局和繪製
  3. 通知渲染層將有新的幀

mChoreographer.postCallback方法的內部的調用鏈
postCallback -> postCallbackDelayed -> postCallbackDelayedInternal -> scheduleFrameLocked -> doFrame(System.nanoTime(), 0) -> doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos)

最終會調用上一部傳入的mTraversalRunnable的run方法 mTraversalRunnable.run -> doTraversal -> performTraversals performTraversals方法是主要的繪製方法。performTraversals會依次執行 performMeasure -> performLayout -> performDraw 完成視圖的幀繪製

相關文章
相關標籤/搜索