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

在一個APP中,其實UI的處理也就是視圖的工作并不是交由Activity來處理的,Activity并不負責視圖控制,它只是控制生命周期和處理事件。真正控制視圖的是Window。一個Activity包含了一個Window,Window才是真正代表一個窗口。Activity就像一個控制器,統籌視圖的添加與顯示,以及通過其他回調方法,來與Window、以及View進行交互。舉個什么例子呢,這似乎就像CPU和顯卡之間的關系,CPU的工作就好像是這里的Activity,而顯卡就好像是Window,主要負責的是視圖相關的工作繪制UI、加載圖像之類的。那么什么是Window呢:

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

Window的創建

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

    WindowManagerGlobal.initialize();

    final Activity a = performLaunchActivity(r, customIntent);

initialize函數主要是在WindowManagerGlobal中獲取WindowManagerService的代理,然后performLaunchActivity會通過反射創建并返回一個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進程與系統進程通信時,獲取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的創建過程中,也會把視圖相關的對象綁定到當前Activity上,現在來看看具體是如何工作的:

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處創建了一個Window對象,之所以是一個PhoneWindow對象,是因為Window是一個抽象類,具體的實現類就是PhoneWindow,然后為Window對象設置各類監聽器和一個WindowManager,activity實現了Window的Callback接口,所以當Window視圖發生變化時會回調Activity的方法。到這里Window的創建就完成了,那么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的實現可以看出,它將具體實現交給了Window,而這個mWindow就是之前的new PhoneWindow,所以這里真正實現的是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樹的根節點
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        
        //回調Activity的onContentChanged方法通知視圖發生改變
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

首先是調用installDecor方法在PhoneWindow內部創建一個DecorView:

創建并初始化DecorView

 private void installDecor() {
        mForceDecorInstall = false;
        //通過generateDecor方法創建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,具體的布局與設置的主題和系統版本有關
        //最終返回一個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方法直接創建一個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的根節點。這里我么能通過查看系統布局文件,可以發現這個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>

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

PhoneWindow的setContentView大概流程就是:首先創建DecorView和mContentParent,并將mContentParent添加到DecorView中,然后將解析出來的View以mContentParen為根節點添加到mContentParent中即添加到了DecorView中,最后回調Activity中的方法通知視圖發生改變。

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

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

添加Window以顯示視圖

當View添加到了創建的DecorView上之后,這些View還沒有真正顯示到屏幕上,接下來就分析這個顯示流程。那么在Activity的啟動過程中,在哪一個環節實現的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方法執行生命周期中的onResume過程
        //通過token返回之前創建的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設置為不可見狀態
            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狀態改變就調用回調方法
                    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();
            }
        }
        ···
    }

這一步首先會執行handleResumeActivity方法完成生命周期的onResume方法,然后獲取前面創建的ActivityClientRecord、PhoneWindow、DecorView、WindowManager等對象并調用WindowManager的addView方法;WindowMnager設個抽象類,實現類是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);
    }
}

可以發現WindowManagerImpl并沒有實現addView方法,而是交給了WindowManagerGlobal來處理;實際上除了addView添加View方法以外,更新視圖的updataViewLayout和刪除View的removeView方法也都交給了了WindowManagerGlobal來處理。WindowManagerImpl這種實現方式是典型的橋接模式,將所有的工作都委托給了WindowManagerGlobal來實現。現在看看WindowManagerGlobal的addView的實現:

    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");
        }
        //如果是子Window還需要調整參數
        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所對應的布局參數,而mDyingViews存儲的是那些正在被刪除的View對象,也就是被調用removeView方法還沒執行完成。注釋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方法實現異步刷新請求,該方法內部會開始View的繪制流程:

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

這里首先檢查是否是創建View的線程,因為只有創建這些View的線程才可以操作這些View,然后調用了scheduleTraversals方法開始View的繪制流程,這里就不往里看了;但由于其內部實現方式View的繪制流程實際上在WindowSession的addToDisplay之后執行,這部分之后再細談。接著看注釋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構造方法中被創建的,也就是之前WindowManagerGlobal創建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函數創建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) {
               ...
            }
         ...

        // 創建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函數
        win.attach();
        mWindowMap.put(client.asBinder(), win);

        ...

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

    return res;
}

上面函數主要的工作就是創建了一個WindowState對象,并調用了它的attach函數:

// 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方法,內部創建了一個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進程交互的應用程序端都需要創建一個SurfaceSession對象。

小結

到這里addToDisplay方法就分析完了,Window的添加請求是交給了WindowManagerService來處理的,具體的添加細節與實現原理就不再深入探索了,本文主要了解Window在啟動過程的添加流程。

歡迎訪問個人博客

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容