1. 程式人生 > >從頭到尾給你講明白Android View實現原理

從頭到尾給你講明白Android View實現原理

Activity是Android開發者寫第一行程式碼起就開始接觸到的。而在onCreate方法中呼叫setContentView(R.layout.main_activity),恐怕也是絕大多數開發者的頭等任務。然後我們可以呼叫findViewById(R.id.xxx)來獲取佈局中的某一個View。通過給View設定點選事件的監聽來響應使用者的操作。就這麼簡單我們和Android的View過了一段幸福的時光,直到有一天,在某個編碼過後的深夜,在剛要合上電腦的一剎那,View低沉卻又如心頭一擊的一句話,打破了很長時間以來看似平淡幸福的生活局面,“喂,我們在一起這麼久了,可是你真的瞭解我嗎?”。是啊,我瞭解View嗎,它從哪裡來?它和Activity的關係是什麼?為什麼有時和它相處,它的脾氣好像總是有點怪,比如在onCreate方法中獲取View的width,一直為0?還有onMeasure onLayout onDraw方法是從什麼時候開始呼叫的?好吧是時候帶你一起來認識認識你覺得熟悉,卻又不是那麼熟悉的View。

在開始本文前,還是按照慣例,丟擲幾個問題讓讀者思考,讓讀者帶著思考來閱讀文章,一來可以讓讀者更有針對性,二來也可以反映出本文的大概內容,讓讀者一眼就能看到文章內容的大概

  1. setContentView一般都是在onCreate中呼叫,可以在onResume中呼叫嗎?
  2. Activity的ContentView是什麼時候在Activity上顯示給使用者看的
  3. Window、Activity、View他們三者之間的關係是什麼
  4. WindowManager是什麼,它和View之間的關係是什麼
  5. WindowManagerService在整個View體系中充當什麼角色
  6. ViewRootImpl是什麼,它是什麼時候建立的,它與View之間的關係是什麼
  7. View的measure、onMeasure、layout、onLayout、draw、onDraw是什麼意思,它們之間有什麼關係
  8. View的MeasureSpec是什麼,它是怎麼控制View和子View的測量的
  9. View的requestLayout和invalidate區別是什麼

上面9個問題,你可能平時或多或少都有思考過。如果這些問題你都能答上來,那麼恭喜你,你對View的掌握可以說是通透了。如果不是,那麼請跟我一起探尋View的祕密

1. Activity.setContentView中發生了什麼

1.1 Activity.setContentView

首先我們來看一下原始碼,因為只有兩行,所以直接貼程式碼吧

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

1.2. Activity.getWindow

原來是呼叫了getWindow().setContentView(layoutResID),好吧順藤摸瓜,看看getWindow()原始碼

public Window getWindow() {
        return mWindow;
    }

1.3. Activity.attach

好了,就不跟你貼mWindow的定義的原始碼了,請你直接翻閱原始碼,mWindow是Window物件。具體來說是PhoneWindow物件。接下來我們看下mWindow在什麼時候初始化的,查詢一番,發現是在Activity的attach方法中,被賦值的,如果你對attach方法感覺到陌生又好奇,可以參考這篇文章。在這裡我也對attach方法做一個簡單的講解,省得讀者被中斷。簡單來說Activity並不是一個真正的Context物件,Activity只是有名無實,真正的Context物件是通過呼叫Activity的attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window)方法賦值給Activity的。同時在該方法中,初始化了mWindow物件

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...省略一些程式碼
        //呼叫WindowManagerImpl.createLocalWindowManager返回WindowManagerImpl
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            //如果mParent不為空
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();

    }

1.4. PhoneWindow和WindowManger

從上面Activity.attach方法中,我們看到mWindow = new PhoneWindow(this,window)、mWindow.setWindowManager()和mWindowManager = mWindow.getWindowManager()這幾行比較重要的程式碼。首先我們來看下PhoneWindow的原始碼,看下PhoneWindow.setContentView做了什麼。關於WindowManager我們在介紹完了PhoneWindow後我們再來講解

1.5. PhoneWindow.setContentView

 @Override
    public void setContentView(int layoutResID) {

        //如果mContentParent==null初始化DecorView
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

乍一看這個方法看起來也很簡單啊,只是mContentParent這是個什麼?好吧,先留下這個問題,接著看installDecor方法

1.6 PhoneWindow.installDecor

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //return new DecorView
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        //如果mContentParent==null 初始化mContentParent
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...省略程式碼
            }
        }
    }

好吧這裡還引出一個DecorView mDecor物件。我們看下DecorView的原始碼可以看到它是FrameLayout的子類。關於mDecor和mContentParent物件我們可以這樣認為。mDecor物件代表的PhoneWindow的根View。而mContentParent物件是mDecor物件的子View。而mContentParent是Activity.setContentView的contentView的父View。由此我們可以知道。Activity的contentView是依附在PhoneWindow的DecorView上的。Activity的ActionBar之類的控制元件也正是PhoneWindow代為操勞的。

好吧,至此我們應該明白一個道理,Activity裡所有使用者肉眼能夠看到的元件,其實都是依附在PhoneWindow上。你可以把PhoneWindow簡單認為是Activity的ContentView。那麼PhoneWindow是如何被新增到Activity上被使用者看到的呢?答案是通過WindowManager。

1.7 WindowManager和WindowMangerService

WindowMangerService是一個系統級的服務,在開機的時候,系統會啟動該服務。至於WindowManagerService的實現原理不是本文的任務。WindowManager和WindowMangerService的關係 與ActivityManager和ActivityManagerService是一樣的。WMS是系統級的服務,它是真正將View新增到手機螢幕上展示給使用者看的。而WindowManger你可以簡單認為是WMS執行在應用程式端的遠端代理物件(真正的代理物件是WindoManagerGlobal的sWindowManagerService物件)。因為我們的應用程式是無法直接直接訪問WMS等系統服務,需要通過AIDL來實現跨程序通訊。WindowManager和WMS正是AIDL中的遠端代理物件和本地服務物件(不懂AIDL的同學需要去補補課喲,這裡預設大家都懂了)。通過WindowManager.addView(View view, ViewGroup.LayoutParams params)可以把View展示給使用者

1.8 Window.setWindowManager

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

WindowManager mWindowManager物件原來是WindowManagerImpl.createLocalWindowManager方法生成的

1.9 WindowManagerImpl.createLocalWindowManager

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

原來是直接new WindowManagerImpl物件啊。好吧Activity的mWindowManager原來是WindowManagerImpl物件,好吧我們來看下WindowManagerImpl原始碼

1.10 WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }


    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

原來WindowManagerImpl addView是藉助WindowManagerGlobal mGlobal來實現的。有經驗的老司機一眼就能看出WindowManagerGlobal實現了單例模式

1.11 WindowManagerGlobal原始碼

public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";

    //WindowManagerGlobal單例物件
    private static WindowManagerGlobal sDefaultWindowManager;
    //WindowManagerService在客戶端的遠端代理物件
    private static IWindowManager sWindowManagerService;
    //單個App客戶端與WindowManagerService對應的一個IWindowSession
    private static IWindowSession sWindowSession;

    private final Object mLock = new Object();

    //App程序中所有Activity的DecorView
    private final ArrayList<View> mViews = new ArrayList<View>();

    //App程序中所有Activity的DecorView對應的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    //要結束生命的Views
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

    private Runnable mSystemPropertyUpdater;

    private WindowManagerGlobal() {
    }

    public static void initialize() {
        getWindowManagerService();
    }

    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

    /**獲取WindowMangerService在APP程序端的遠端物件**/
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

    /**
    *   1.如果view已經被add了,報錯
    *   2.建立ViewRootImpl,呼叫ViewRootImpl.setView
    **/
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            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);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            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 {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }



    private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

    void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
            doTrimForeground();
        }
    }

    private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

}

總結WindowManagerGlobal功能如下

  1. 獲取WMS的遠端代理物件
  2. 獲取WMS的IWindowSession物件
  3. 建立ViewRootImpl物件
  4. 通過ViewRootImpl物件來addView removeView updateViewLayout

好吧,至此我們引出了View體系中至關重要的ViewRootImpl物件,我們放在一講講解,我們回頭再想想,在Activity的onCreate中呼叫setContentView,生成的PhoneWindow的DecorView物件什麼時候被WindowManger呼叫addView的呢

2. ActivityThread.handleResumeActivity 中將PhoneWindow的DecorView add到WindowManger上

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        //呼叫Activity的onResume方法
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            //如果不可見說明在啟動另一個Activity
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                //PhoneWindow 在Activity的attach中初始化的
                r.window = r.activity.getWindow();

                View decor = r.window.getDecorView();
                //剛開始的時候 設定成不可見
                decor.setVisibility(View.INVISIBLE);

                //返回的是WindowManagerImpl
                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();
                    // 第一次應該是為null
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //這裡會把decor載入到WindowManagerImpl中
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

            // 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) {
                if (r.newConfig != null) {
                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

從原始碼中我們看到在handleResumeActivity中通過wm.add(decor,l)把Activity的DecorView載入到WindowManger上的。從ActivityThread中處理Activity的生命週期順序是 onCreate -> onResume -> wm.add(decor,l)。所以這裡能解釋不少問題了,1.可以在onResume中呼叫setContentView 2.在onCreate中呼叫View.getWidth等方方法是返回不了正確值的。

3. ViewRootImpl.setView

3.1 ViewRootImpl.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            //如果mView==null 表示是新增加的
            if (mView == null) {
                mView = view;

                ...省略
                requestLayout();

                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }


                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not for an application");
                        case WindowManagerGlobal.ADD_APP_EXITING:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- app for token " + attrs.token
                                    + " is exiting");
                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- window " + mWindow
                                    + " has already been added");
                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                            // Silently ignore -- we would have just removed it
                            // right away, anyway.
                            return;
                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- another window of type "
                                    + mWindowAttributes.type + " already exists");
                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- permission denied for window type "
                                    + mWindowAttributes.type);
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified display can not be found");
                        case WindowManagerGlobal.ADD_INVALID_TYPE:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified window type "
                                    + mWindowAttributes.type + " is not valid");
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

            }
        }
    }

上面我保留了兩行比較重要的方法requestLayout()和 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);

requestLayout()主要是讓View經歷measure layout draw 三個階段,mWindowSession.addToDisplay應該是交給WMS去請求其他服務渲染介面。我們主要來看下requestLayout()

我們來跟蹤下requestLayout原始碼

3.2 ViewRootImpl.requestLayout

 @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            //注意這個變數
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

注意mLayoutRequested = true,只有mLayoutRequested為true,measure layout draw三個階段才會全部經歷,否則只會經歷draw階段,這也就是View的reqeustLayout和invalidate的區別,requestLayout會經歷完整的三個階段

3.3 ViewRootImpl.scheduleTraversals()

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

從原始碼中可以看出交由mChoreographer物件去執行mTraversalRunnable物件

3.4 ViewRootImpl的TraversalRunnable物件

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

最終呼叫了ViewRootImpl.doTraversal()

3.5 ViewRootImpl.doTraversal()

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

跟蹤performTraversals()程式碼

3.6 ViewRootImpl.performTraversals()

    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }

        if (host == null || !mAdded)
            return;

        //正在遍歷
        mIsInTraversal = true;
        //馬上要畫
        mWillDrawSoon = true;
        //windowSize是否改變
        boolean windowSizeMayChange = false;
        boolean newSurface = false;
        boolean surfaceChanged = false;
        WindowManager.LayoutParams lp = mWindowAttributes;

        //預期的window的寬度
        int desiredWindowWidth;
        int desiredWindowHeight;

        //當前View是否可見
        final int viewVisibility = getHostVisibility();

        //view的可見性是否改變(1.不是第一次而且和上次的可見性不一樣)
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
        //使用者肉眼看到的View的可見性是否改變
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));

        ...省略程式碼

        Rect frame = mWinFrame;
        if (mFirst) {
            //如果是第一次
            mFullRedrawNeeded = true;
            //需要請求layout
            mLayoutRequested = true;

            //如果是狀態列或者輸入法 需要使用真實的size
            if (shouldUseDisplaySize(lp)) {
                // NOTE -- system code, won't try to do compat mode.
                Point size = new Point();
                mDisplay.getRealSize(size);
                desiredWindowWidth = size.x;
                desiredWindowHeight = size.y;
            } else {
                Configuration config = mContext.getResources().getConfiguration();
                desiredWindowWidth = dipToPx(config.screenWidthDp);
                desiredWindowHeight = dipToPx(config.screenHeightDp);
            }

            ...省略程式碼

            //如果是第一次 會呼叫View的onAttachedToWindow
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
            //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);

        } else {
            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                mFullRedrawNeeded = true;
                mLayoutRequested = true;
                windowSizeMayChange = true;
            }
        }

        if (viewVisibilityChanged) {//如果hostView的可見性變了 說明window的可見性變了
            mAttachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
            if (viewUserVisibilityChanged) {//如果肉眼可見性變了
                host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
            }
            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                endDragResizing();
                destroyHardwareResources();
            }
            if (viewVisibility == View.GONE) {
                // After making a window gone, we will count it as being
                // shown for the first time the next time it gets focus.
                mHasHadWindowFocus = false;
            }
        }

        // Non-visible windows can't hold accessibility focus.
        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
            //如果window不可見把Accessibility clear掉
            host.clearAccessibilityFocus();
        }

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(mAttachInfo.mHandler);

        boolean insetsChanged = false;

        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);

        //如果是requestLayout
        if (layoutRequested) {

            final Resources res = mView.getContext().getResources();

            if (mFirst) {
                // make sure touch mode code executes by setting cached value
                // to opposite of the added touch mode.
                mAttachInfo.mInTouchMode = !mAddedTouchMode;
                ensureTouchModeLocally(mAddedTouchMode);
            } else {
                if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
                            + mAttachInfo.mVisibleInsets);
                }
                if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
                    insetsChanged = true;
                }
                if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                    insetsChanged = true;
                }
                if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                        || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    windowSizeMayChange = true;

                    if (shouldUseDisplaySize(lp)) {
                        // NOTE -- system code, won't try to do compat mode.
                        Point size = new Point();
                        mDisplay.getRealSize(size);
                        desiredWindowWidth = size.x;
                        desiredWindowHeight = size.y;
                    } else {
                        Configuration config = res.getConfiguration();
                        desiredWindowWidth = dipToPx(config.screenWidthDp);
                        desiredWindowHeight = dipToPx(config.screenHeightDp);
                    }
                }
            }


            //1.開始測量
            windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
        }

        ...省略程式碼



        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw