在一個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在啟動過程的添加流程拓提。