淺談Window視圖綁定與工作原理

在一個APP中,其實UI的處理也就是視圖的工作并不是交由Activity來處理的渠欺,Activity并不負責視圖控制虱颗,它只是控制生命周期和處理事件。真正控制視圖的是Window聪黎。一個Activity包含了一個Window罕容,Window才是真正代表一個窗口。Activity就像一個控制器稿饰,統(tǒng)籌視圖的添加與顯示锦秒,以及通過其他回調方法,來與Window喉镰、以及View進行交互旅择。舉個什么例子呢,這似乎就像CPU和顯卡之間的關系侣姆,CPU的工作就好像是這里的Activity生真,而顯卡就好像是Window,主要負責的是視圖相關的工作繪制UI铺敌、加載圖像之類的汇歹。那么什么是Window呢:

  • Window是一個抽象的概念,它是所有View的直接管理者偿凭,任何視圖都需要通過Window來呈現(xiàn)在界面上产弹,當然除了單純的界面視圖的處理以外,視圖的邏輯部分Window也會涉及處理弯囊,就像View的事件分發(fā)一樣痰哨,中間還是需要經過一層的Window的處理

Window的創(chuàng)建

上次簡單分析完了App啟動流程,在流程最后的activity的創(chuàng)建部分匾嘱,調用了activity.attach方法斤斧,我們只是簡單的提了一下是關于Window的創(chuàng)建與綁定,現(xiàn)在我們就以這個方法為入口來看看Window與APP視圖的工作原理

    WindowManagerGlobal.initialize();

    final Activity a = performLaunchActivity(r, customIntent);

initialize函數(shù)主要是在WindowManagerGlobal中獲取WindowManagerService的代理,然后performLaunchActivity會通過反射創(chuàng)建并返回一個Activity對象,先看initialize方法吧:

public static void initialize() {
        getWindowManagerService();
    }
public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                //通過向ServiceManager查詢一個字符串為window的key返回一個IBinder對象
                //然后asInterface方法又會返回一個本地代理
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        //設置動畫
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

這里是一個doubleCheck方式的單例霎烙,返回了一個WinowManagerService的代理撬讽,通過IWindowManager.Stub.asInterface方法獲取到的,這很顯然是AIDL的方式悬垃,這與App進程與系統(tǒng)進程通信時游昼,獲取ActivityManagerService代理是不是簡直一模一樣呢。然后再看performLaunchActivity方法:

    //這里省略大部分代碼
     // 調用activity的attach方法
    activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
    ··· ···

也就是說在Activity的創(chuàng)建過程中尝蠕,也會把視圖相關的對象綁定到當前Activity上烘豌,現(xiàn)在來看看具體是如何工作的:

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, ActivityConfigCallback activityConfigCallback) {
    //注釋1
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
        
    //注釋2
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    ...

    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    ....
}

這里還是只留下了與今天主題相關部分的代碼,首先注釋1處會為activity綁定上下文看彼,注釋2處創(chuàng)建了一個Window對象廊佩,之所以是一個PhoneWindow對象囚聚,是因為Window是一個抽象類,具體的實現(xiàn)類就是PhoneWindow标锄,然后為Window對象設置各類監(jiān)聽器和一個WindowManager顽铸,activity實現(xiàn)了Window的Callback接口,所以當Window視圖發(fā)生變化時會回調Activity的方法料皇。到這里Window的創(chuàng)建就完成了跋破,那么Window是怎么接管Activity的視圖的,Window又是如何附屬在Activity上的瓶蝴,下面開始分析毒返。

Window與Activity間的依附

Activity的視圖部分添加一般都會在onCreate方法里調用setContentView方法,那么我們只需要看setContentView方法即可:

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

從Activity的setContentView的實現(xiàn)可以看出舷手,它將具體實現(xiàn)交給了Window拧簸,而這個mWindow就是之前的new PhoneWindow,所以這里真正實現(xiàn)的是PhoneWindow的setContentView方法:

@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) {
            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 {
         // 將View添加到DecorView的mContentParent中
         // 調用LayoutInflater的inflate方法解析布局文件男窟,并生成View樹盆赤,mContentParent為View樹的根節(jié)點
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        
        //回調Activity的onContentChanged方法通知視圖發(fā)生改變
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

首先是調用installDecor方法在PhoneWindow內部創(chuàng)建一個DecorView:

創(chuàng)建并初始化DecorView

 private void installDecor() {
        mForceDecorInstall = false;
        //通過generateDecor方法創(chuàng)建DecorView
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        //通過generateLayout加載布局文件到DecorView,具體的布局與設置的主題和系統(tǒng)版本有關
        //最終返回一個ViewGroup對象并賦值給mContentParent
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }

                ···
            } 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);
                    }
                }
            }

            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }
           ···
            }
        }
    }

首先會通過generateDecor方法直接創(chuàng)建一個DecorView對象歉眷,然后在調用generateLayout賦值給mContentParent:

protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
    
protected ViewGroup generateLayout(DecorView decor) {
   ....

   mDecor.startChanging();
   mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

   ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

   ....

   mDecor.finishChanging();

   return contentParent;
}

這里findViewById(ID_ANDROID_CONTENT)通過這個Id找到是哪個View呢牺六,其實就是mContentParent,這個ViewGroup是整個contentView中所有view的根節(jié)點汗捡。這里我么能通過查看系統(tǒng)布局文件淑际,可以發(fā)現(xiàn)這個View是一個FrameLayout:

screen_simple.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

其實除了這個文件以外的其他系統(tǒng)布局文件都會有這個id為content的FrameLayout,我們在Activity中設置的布局文件就會依附在這個FrameLayout上面扇住,而整個布局屬于decoerView春缕,通常是一個LinearLayout。

PhoneWindow的setContentView大概流程就是:首先創(chuàng)建DecorView和mContentParent艘蹋,并將mContentParent添加到DecorView中锄贼,然后將解析出來的View以mContentParen為根節(jié)點添加到mContentParent中即添加到了DecorView中,最后回調Activity中的方法通知視圖發(fā)生改變女阀。

這里我畫了一張圖來描述整個關系:

[圖片上傳失敗...(image-e1ba44-1565264534919)]

添加Window以顯示視圖

當View添加到了創(chuàng)建的DecorView上之后宅荤,這些View還沒有真正顯示到屏幕上,接下來就分析這個顯示流程浸策。那么在Activity的啟動過程中冯键,在哪一個環(huán)節(jié)實現(xiàn)的View的顯示的呢,其實是在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
        //調用了performResumeActivity方法執(zhí)行生命周期中的onResume過程
        //通過token返回之前創(chuàng)建的ActivityClientRecord對象
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ···
        final Activity a = r.activity;
        ···
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            //獲取activity的window對象
            r.window = r.activity.getWindow();
            //獲取decorView
            View decor = r.window.getDecorView();
            //decorView設置為不可見狀態(tài)
            decor.setVisibility(View.INVISIBLE);
            //獲取WindowManager對象
            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();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //WindowManager的adView方法添加decorView
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    //如果decorview被設置過了的話的榛,那么Window狀態(tài)改變就調用回調方法
                    a.onWindowAttributesChanged(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;
        }
            ···
            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();
            }
        }
        ···
    }

這一步首先會執(zhí)行handleResumeActivity方法完成生命周期的onResume方法琼了,然后獲取前面創(chuàng)建的ActivityClientRecord逻锐、PhoneWindow夫晌、DecorView雕薪、WindowManager等對象并調用WindowManager的addView方法;WindowMnager設個抽象類,實現(xiàn)類是WindowMnagerImpl,所以真正調用的是它的addView方法:

public final class WindowManagerImpl implements WindowManager {
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

可以發(fā)現(xiàn)WindowManagerImpl并沒有實現(xiàn)addView方法晓淀,而是交給了WindowManagerGlobal來處理;實際上除了addView添加View方法以外所袁,更新視圖的updataViewLayout和刪除View的removeView方法也都交給了了WindowManagerGlobal來處理。WindowManagerImpl這種實現(xiàn)方式是典型的橋接模式凶掰,將所有的工作都委托給了WindowManagerGlobal來實現(xiàn)≡镆現(xiàn)在看看WindowManagerGlobal的addView的實現(xiàn):

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        //檢查參數(shù)是否合法
        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");
        }
        //如果是子Window還需要調整參數(shù)
        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;

        ···
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //注釋1
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            //調用ViewRootImpl來更新界面
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

看到注釋1處,首先需要介紹幾個WindowManagerGlobal中比較重要的成員變量:

private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

上面的這幾個變量中懦窘,mViews存儲的是所有Window所對應的View前翎,mRoots存儲的是所有Window所對應的ViewRootImpl,mParams存儲的是所有Window所對應的布局參數(shù)畅涂,而mDyingViews存儲的是那些正在被刪除的View對象港华,也就是被調用removeView方法還沒執(zhí)行完成。注釋1處就會將Window中的一些對象存儲到上述容器中午衰。然后更新界面完成Window的添加立宜,調用了ViewRootImpl的setView方法。

更新界面完成Window添加

了解過View的繪制的應該知道臊岸,該操作也是由ViewRootImpl來完成的橙数,這里也不例外。通過調用setView方法然后內部調用requestLayout方法來完成異步刷新請求:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                //省略部分代碼
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // 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.
                //異步刷新請求 開始View的繪制流程
                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();
                    //注釋1
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, 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 (mTranslator != null) {
                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
                }
                mPendingOverscanInsets.set(0, 0, 0, 0);
                mPendingContentInsets.set(mAttachInfo.mContentInsets);
                mPendingStableInsets.set(mAttachInfo.mStableInsets);
                mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                mPendingVisibleInsets.set(0, 0, 0, 0);
                mAttachInfo.mAlwaysConsumeNavBar =
                        (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
                mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
                if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                 ···

                if (view instanceof RootViewSurfaceTaker) {
                    mInputQueueCallback =
                        ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
                }
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                view.assignParent(this);
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;

                if (mAccessibilityManager.isEnabled()) {
                    mAccessibilityInteractionConnectionManager.ensureConnection();
                }

                if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                    view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                }

                // Set up the input pipeline.
                CharSequence counterSuffix = attrs.getTitle();
                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;
            }
        }
    }

setView內部通過requestLayout方法實現(xiàn)異步刷新請求帅戒,該方法內部會開始View的繪制流程:

public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            //View繪制流程的入口
            scheduleTraversals();
        }
    }

這里首先檢查是否是創(chuàng)建View的線程灯帮,因為只有創(chuàng)建這些View的線程才可以操作這些View,然后調用了scheduleTraversals方法開始View的繪制流程逻住,這里就不往里看了施流;但由于其內部實現(xiàn)方式View的繪制流程實際上在WindowSession的addToDisplay之后執(zhí)行,這部分之后再細談鄙信。接著看注釋1處瞪醋,通過調用mWindowSession的addToDisplay方法來完成最終的Window的添加過程:

// frameworks/base/services/core/java/com/android/server/wm/Session.java
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
    int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
    Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
        outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

mWindowSession是一個IWindowSession對象,也就是一個Binder装诡,它是在ViewRootImpl構造方法中被創(chuàng)建的银受,也就是之前WindowManagerGlobal創(chuàng)建ViewRootImpl的時候。每個應用進程中只會有一個mWindowSession對象鸦采,WMS為每一個應用進程分配一個Session對象宾巍,應用進程通過這個對象與WindowManagerService通信。所以這里調用了WindowManagerService的addWindow方法

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
    int[] appOp = new int[1];
    int res = mPolicy.checkAddPermission(attrs, appOp);
    if (res != WindowManagerGlobal.ADD_OKAY) {
        return res;
    }

    boolean reportNewConfig = false;
    WindowState parentWindow = null;
    long origId;
    final int callingUid = Binder.getCallingUid();
    final int type = attrs.type;

    synchronized(mWindowMap) {
        if (!mDisplayReady) {
            throw new IllegalStateException("Display has not been initialialized");
        }
        // 獲取DisplayContent
        final DisplayContent displayContent = getDisplayContentOrCreate(displayId);

        ....

        AppWindowToken atoken = null;
        final boolean hasParent = parentWindow != null;
        // 調用displayContent的getWindowToken函數(shù)創(chuàng)建WindowToken
        // WindowToken保存在displayContent的mTokenMap哈希表中
        // Use existing parent window token for child windows since they go in the same token
        // as there parent window so we can apply the same policy on them.
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        // If this is a child window, we want to apply the same type checking rules as the
        // parent window type.
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        boolean addToastWindowRequiresToken = false;

        if (token == null) {
            ....

            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            final boolean isRoundedCornerOverlay =
                    (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
            // 如果未能從displayContent獲取到WindowToken則新建一個WindowToken
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
        } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            // 如果Window的類型為APPLICATION_WINDOW則將WindowToken轉為AppWindowToken
            atoken = token.asAppWindowToken();
            if (atoken == null) {
               ...
            }
         ...

        // 創(chuàng)建WindowState保存Window
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);

        ...

        // 為Window注冊InputChannel
        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }
        ...

        // 調用WindowState的attach函數(shù)
        win.attach();
        mWindowMap.put(client.asBinder(), win);

        ...

        // 將新建的WindowState添加到WindowContainer的管理中渔伯,WindowContainer是WindowToken的父類
        win.mToken.addWindow(win);
        ...

    return res;
}

上面函數(shù)主要的工作就是創(chuàng)建了一個WindowState對象顶霞,并調用了它的attach函數(shù):

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java    
void attach() {
    if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
    mSession.windowAddedLocked(mAttrs.packageName);
}

調用了Session的windowAddedLocked方法,內部創(chuàng)建了一個SurfaceSession對象:

// frameworks/base/services/core/java/com/android/server/wm/Session.java
void windowAddedLocked(String packageName) {
    mPackageName = packageName;
    mRelayoutTag = "relayoutWindow: " + mPackageName;
    if (mSurfaceSession == null) {
        if (WindowManagerService.localLOGV) Slog.v(
            TAG_WM, "First window added to " + this + ", creating SurfaceSession");
        mSurfaceSession = new SurfaceSession();

        if (SHOW_TRANSACTIONS) Slog.i(
                TAG_WM, "  NEW SURFACE SESSION " + mSurfaceSession);
        mService.mSessions.add(this);
        if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
            mService.dispatchNewAnimatorScaleLocked(this);
        }
    }
    mNumWindow++;
}

SurfaceSession對象承擔了應用程序與SurfaceFlinger之間的通信過程,每一個需要與SurfaceFlinger進程交互的應用程序端都需要創(chuàng)建一個SurfaceSession對象选浑。

小結

到這里addToDisplay方法就分析完了蓝厌,Window的添加請求是交給了WindowManagerService來處理的,具體的添加細節(jié)與實現(xiàn)原理就不再深入探索了古徒,本文主要了解Window在啟動過程的添加流程拓提。

歡迎訪問個人博客

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市隧膘,隨后出現(xiàn)的幾起案子代态,更是在濱河造成了極大的恐慌,老刑警劉巖疹吃,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹦疑,死亡現(xiàn)場離奇詭異,居然都是意外死亡萨驶,警方通過查閱死者的電腦和手機必尼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篡撵,“玉大人判莉,你說我怎么就攤上這事∮” “怎么了券盅?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長膛檀。 經常有香客問我锰镀,道長,這世上最難降的妖魔是什么咖刃? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任泳炉,我火速辦了婚禮,結果婚禮上嚎杨,老公的妹妹穿的比我還像新娘花鹅。我一直安慰自己,他們只是感情好枫浙,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布刨肃。 她就那樣靜靜地躺著,像睡著了一般箩帚。 火紅的嫁衣襯著肌膚如雪真友。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天紧帕,我揣著相機與錄音盔然,去河邊找鬼。 笑死,一個胖子當著我的面吹牛愈案,可吹牛的內容都是我干的挺尾。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼刻帚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涩嚣?” 一聲冷哼從身側響起崇众,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎航厚,沒想到半個月后顷歌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡幔睬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年眯漩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麻顶。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡赦抖,死狀恐怖,靈堂內的尸體忽然破棺而出辅肾,到底是詐尸還是另有隱情队萤,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布矫钓,位于F島的核電站要尔,受9級特大地震影響,放射性物質發(fā)生泄漏新娜。R本人自食惡果不足惜赵辕,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望概龄。 院中可真熱鬧还惠,春花似錦、人聲如沸私杜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歪今。三九已至嚎幸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寄猩,已是汗流浹背嫉晶。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人替废。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓箍铭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椎镣。 傳聞我的和親對象是個殘疾皇子诈火,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容