前言
好久沒有繼續(xù)寫Android重學系列了昼捍。這次我們繼續(xù)聊聊當Activity創(chuàng)建之后烟勋。Android接下來就會嘗試的顯示界面ui。此時就會牽扯到一個核心的服務WindowManagerService(當然Activity的啟動也牽扯到了WMS,兩者是互相糾纏但是職能不同),窗口管理服務偏化。我原本是想和大家聊聊SurfaceFlinger,但是可惜的是匪凡,還沒到時候严衬,而且屬于學習起來需要一點OpenGL的知識,因此換一個方向抚笔,從上至下一點點剖析整個Android的顯示系統(tǒng)扶认。
因為Android的窗口也是顯示系統(tǒng)中的一員,同時也在Activity啟動中也嵌入了不少邏輯殊橙。因此本文將會從WMS的啟動以及Activity的啟動WMS在其中的角色這兩個角度辐宾,來聊聊WMS在Android中的職能狱从。
如果遇到問題請在http://www.reibang.com/p/1fd180ea5d0e
聯系本人,歡迎討論。
注意以下源碼全部來自Android9.0.
正文
概論
在重學系列的Android啟動中叠纹,我介紹核心類ActivityRecord在整個Android系統(tǒng)中的變換與流轉季研。但是并沒有涉及這個Activity怎么顯示到屏幕上的。也就說并沒有提及WindowManagerService(以后稱為WMS)誉察,系統(tǒng)窗口服務与涡。更加沒有提及SurfaceFlinger這個系統(tǒng)渲染類。
Android的顯示系統(tǒng)是一個十分大的體系持偏。本文作為Android顯示系統(tǒng)的第一篇驼卖,作為一個Android開發(fā),時刻接觸UI鸿秆,我們有必要聊聊這個體系中酌畜,各個核心類的職責。
WMS是做什么的谬莹?WMS顧名思義就是系統(tǒng)的窗口管理者檩奠。
窗口是什么?從Activity用戶交互界面角度看來附帽,就是指應用的頁面窗口埠戳。而從底層的SurfaceFlinger來看,WMS管理的每一個窗口都是一個可以從中獲取到像素數據通過GPU/CPU渲染到屏幕的Layer蕉扮。從WMS角度來看整胃,每一個窗口都是一個WindowState,用于管理窗口狀態(tài)喳钟。從每一個View來看屁使,View的根布局存在一個ViewRootImpl,所有的view的渲染像素都保存在ViewRootImpl的Surface對象中奔则。從I/O系統(tǒng)來看蛮寂,WMS還必須響應觸屏,鍵盤等事件的派發(fā)易茬。
WMS的啟動
廢話不多說酬蹋,讓我們先粗略的看看WMS的啟動,我們能夠從啟動中窺探到WMS大致上控制了什么抽莱。還記的SystemServer啟動的時候范抓,我們初始化了很多服務嗎,其中就有初始化WMS:
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
能看到此時WMS將會調用main方法食铐,把InputManager(事件分發(fā)服務)傳遞到WMS中匕垫,并且生成了Window的窗口策略PhoneWindowManager。這個PhoneWindowManager實際上包含了計算窗口大小等策略虐呻,是一個核心類象泵。
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0);
return sInstance;
}
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
installLock(this, INDEX_WINDOW);
mContext = context;
...
LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
mDisplaySettings.readSettingsLocked();
mPolicy = policy;
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
if(mInputManager != null) {
final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
mPointerEventDispatcher = inputChannel != null
? new PointerEventDispatcher(inputChannel) : null;
} else {
mPointerEventDispatcher = null;
}
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
...
mScreenFrozenLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
mAppTransition = new AppTransition(context, this);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
final AnimationHandler animationHandler = new AnimationHandler();
animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
AnimationThread.getHandler(), animationHandler);
mActivityManager = ActivityManager.getService();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
...
mPmInternal = LocalServices.getService(PackageManagerInternal.class);
final IntentFilter suspendPackagesFilter = new IntentFilter();
...
// Get persisted window scale setting
...
mSettingsObserver = new SettingsObserver();
mHoldingScreenWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
mSurfaceAnimationRunner = new SurfaceAnimationRunner();
...
mTaskPositioningController = new TaskPositioningController(
this, mInputManager, mInputMonitor, mActivityManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
LocalServices.addService(WindowManagerInternal.class, new LocalService());
}
能看到啟動如下幾個核心類寞秃。
- 1.WindowAnimator 窗口動畫對象
- 2.RootWindowContainer 根部窗口容器,管理窗口上所有的子窗口
- 3.WindowSurfacePlacer 窗口大小大小測量者
- 4.AnimationHandler Window處理動畫事件的Handler
- 5.PackageManagerInternal 包核心服務
- 6.DisplayManager 顯示器管理者
- 7.PowerManager 電源管理者
- 8.ActivityManager Activity操作相關的管理者
本文也將圍繞RootWindowContainer 的父類WindowContainer 這個類進行講解单芜。
從WMS角度看Android顯示體系的總覽圖
結合上面的構造函數蜕该,我這里弄出了一幅從WMS角度看Android系統(tǒng)的顯示體系。
這里只是一個參考圖洲鸠,實際上在下面這幅圖還有些不夠準確堂淡。但是足以讓人大致上了解到Android顯示體系中,大體的研究方向扒腕,以及核心思想绢淀。
WindowContainer與WindowContainerController
為了之后的邏輯能夠更加輕易的整理。這邊需要先理清楚WMS這兩個核心類瘾腰,WindowContainer,WindowContainerController皆的。比起過去版本的Android系統(tǒng)(如4.4,7.0),Android9.0在窗口管理體系上做了很大的努力的抽象蹋盆。
WindowContainer 在整個WMS中承擔了所有可以看做Window容器的角色费薄,其本身能夠控制所有綁定進來的子WindowContainer。注意了栖雾,這里是指Window容器楞抡,而不是window本身。
WindowContainerController 在整個WMS中承擔著控制WindowContainer的角色析藕。
下面是一個UML圖
能看到整個WMS幾乎所有的核心操作需要核心類都在這里面了召廷。
從名字我們就能窺探到在Activity啟動流程中缺失的Window處理部分。也能從其泛型了解到每一個WindowContainer的關聯账胧。首先我們大致上來聊聊每一個WindowContainer的作用竞慢。
WindowContainer
WindowContainer 作為每一個可以看做是Window的容器的抽象類。其核心作用就是提供共有的處理子WindowConatiner操作治泥。其核心添加子WindowConatiner如下:
protected final WindowList<E> mChildren = new WindowList<E>();
protected void addChild(E child, Comparator<E> comparator) {
if (child.getParent() != null) {
throw new IllegalArgumentException("addChild: container=" + child.getName()
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
int positionToAdd = -1;
if (comparator != null) {
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
if (comparator.compare(child, mChildren.get(i)) < 0) {
positionToAdd = i;
break;
}
}
}
if (positionToAdd == -1) {
mChildren.add(child);
} else {
mChildren.add(positionToAdd, child);
}
onChildAdded(child);
// Set the parent after we've actually added a child in case a subclass depends on this.
child.setParent(this);
}
能看到筹煮,在這個方法中,只要每一種Window容器都有自己的策略居夹,通過這種策略調整插入的位置败潦,最后才會進行插入子Window容器。并且獲得父親Window容器是什么吮播。
實際上這種設計就是一個雙向鏈表,只是更加的復雜眼俊。因此當WindowConatiner1實現的子類確定了什么泛型意狠,說明這種WindowContainer控制的子WindowConatiner的類型也就確定了。
WindowContainer當然有其他重要的操作疮胖,這里先不聊环戈。
DisplayContent
屏幕顯示內容的WindowConatiner闷板。其和邏輯顯示屏幕id綁定在一起。一般的院塞,我們應用程序是基于它之上創(chuàng)建的遮晚。
我們能夠看到其泛型擴展的是DisplayChildWindowContainer:
static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> {
DisplayChildWindowContainer(WindowManagerService service) {
super(service);
}
@Override
boolean fillsParent() {
return true;
}
@Override
boolean isVisible() {
return true;
}
}
實現十分簡單,DisplayContent必定是充滿全父容器拦止,且可見的县遣。注意了,既然是代表的是邏輯上的顯示屏那必定不可能存在子的顯示屏汹族,因此addChlid是禁止的:
@Override
protected void addChild(DisplayChildWindowContainer child,
Comparator<DisplayChildWindowContainer> comparator) {
throw new UnsupportedOperationException("See DisplayChildWindowContainer");
}
@Override
protected void addChild(DisplayChildWindowContainer child, int index) {
throw new UnsupportedOperationException("See DisplayChildWindowContainer");
}
了解這些不足夠讓我們對DisplayContent有一個粗略的了解萧求,讓我們看看構造函數:
DisplayContent(Display display, WindowManagerService service,
WallpaperController wallpaperController, DisplayWindowController controller) {
super(service);
setController(controller);
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
+ " new=" + display);
}
mDisplay = display;
mDisplayId = display.getDisplayId();
mWallpaperController = wallpaperController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
// We use this as our arbitrary surface size for buffer-less parents
// that don't impose cropping on their children. It may need to be larger
// than the display size because fullscreen windows can be shifted offscreen
// due to surfaceInsets. 2 times the largest display dimension feels like an
// appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
// layers the ability to match their parent sizes and be able to skip
// such arbitrary size settings.
mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
.setSize(mSurfaceSize, mSurfaceSize)
.setOpaque(true);
mWindowingLayer = b.setName("Display Root").build();
mOverlayLayer = b.setName("Display Overlays").build();
getPendingTransaction().setLayer(mWindowingLayer, 0)
.setLayerStack(mWindowingLayer, mDisplayId)
.show(mWindowingLayer)
.setLayer(mOverlayLayer, 1)
.setLayerStack(mOverlayLayer, mDisplayId)
.show(mOverlayLayer);
getPendingTransaction().apply();
// These are the only direct children we should ever have and they are permanent.
super.addChild(mBelowAppWindowsContainers, null);
super.addChild(mTaskStackContainers, null);
super.addChild(mAboveAppWindowsContainers, null);
super.addChild(mImeWindowsContainers, null);
// Add itself as a child to the root container.
mService.mRoot.addChild(this, null);
// TODO(b/62541591): evaluate whether this is the best spot to declare the
// {@link DisplayContent} ready for use.
mDisplayReady = true;
}
boolean isReady() {
// The display is ready when the system and the individual display are both ready.
return mService.mDisplayReady && mDisplayReady;
}
此時能看到DisplayContent在構造函數中,首先會把相關的屏幕大小顶瞒,密度夸政,id等信息綁定。
接著實例化SurfaceControl這個對象榴徐。記住這個對象是核心守问,聯通Window和Android渲染核心的類。
所有渲染像素都會保存在Surface中坑资,因此這個實例化告訴Surface是耗帕,此時需要透明,并且此時渲染屏幕的大小公式為:Max(屏幕寬度盐茎,屏幕高度)*2兴垦。
在系統(tǒng)第一次生成Window的時候,Android系統(tǒng)并不希望裁減掉最初當前的顯示范圍字柠。surfaceInsets代表著渲染的范圍探越,此時其實充滿屏幕的窗體能夠移動。因此會選擇最大尺寸*2窑业,讓窗體足夠的控件顯示钦幔。
包含了mWindowingLayer 和mOverlayLayer。OverLayer這些命名應該比較熟悉常柄,實際上有點像View之上的Overlayer一樣鲤氢,為Window動畫做鋪墊。
DisplayContent控制的WindowContainer
接著會把如下幾個WindowConatiner的添加到DisplayContent中:
1.mBelowAppWindowsContainers(類型NonAppWindowContainers)
一切的應該在Activity之下的Window容器西潘,比如wrapper壁紙卷玉。2.mTaskStackContainers(類型TaskStackContainers)
這里面一般是指應用的Activity。實際上也就是我們常說的Activity對應著整個系統(tǒng)棧的管理者3.mAboveAppWindowsContainers(類型AboveAppWindowContainers)
這里是指一切在Activity之上的窗體容器喷市,比如說StatusBar狀態(tài)欄相种。5.mImeWindowsContainers(類型NonMagnifiableWindowContainers)
這里是指如Dialog,輸入鍵盤的窗體容器品姓。
因此我能夠依據順序能夠了解到寝并,實際上在整個Android系統(tǒng)中箫措,一旦開始繪制將會依照如下順序進行繪制一個界面:
最后統(tǒng)統(tǒng)添加到RootWindowContainer中。
當然既然有這些WindowContainer不可能不添加衬潦。這里替代掉addChild方法斤蔓,一般使用addWindowToken來添加到對應的WindowConatiner中。關于addWindowToken詳細會在之后聊到镀岛。
RootWindowContainer
RootWindowContainer 顧名思義弦牡,根部WindowContainer,一切WindowContainer的總管理者哎媚。其泛型確定了是DisplayContent喇伯。因此確切的說,這是專門用來管理邏輯顯示屏幕對應區(qū)域的窗體容器拨与。
當需要大范圍的尋找子Window容器稻据,可以通過RootWindowContainer 進行輪詢查找。
WindowToken
從名字上來看就知道這是一個句柄买喧。這是一個關于Window的句柄捻悯。甚至可以推測WindowToken中肯定包含一個IBinder對象,來對應Android端的Window淤毛。具體這個IBinder是指哪一個今缚,稍后就揭曉。
為了能夠粗略的了解WindowToken低淡,讓我們先看看它的構造函數:
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
onDisplayChanged(dc);
}
在這里面有一個核心的函數onDisplayChanged姓言,當WindowToken生成了,說明應用端有一個新的Window誕生了蔗蹋,需要做addWindow到WindowManager的操作何荚,此時需要告訴DisplayContent,窗體列表需要更新了猪杭。
void onDisplayChanged(DisplayContent dc) {
dc.reParentWindowToken(this);
mDisplayContent = dc;
// The rounded corner overlay should not be rotated. We ensure that by moving it outside
// the windowing layer.
if (mRoundedCornerOverlay) {
mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
}
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
// to another display before the window behind
// it is ready.
super.onDisplayChanged(dc);
}
WindowToken添加到DisplayContent中
此時調用了DisplayContent的reParentWindowToken方法,把當前的WindowToken綁定到DisplayContent中餐塘。
/** Changes the display the input window token is housed on to this one. */
void reParentWindowToken(WindowToken token) {
final DisplayContent prevDc = token.getDisplayContent();
if (prevDc == this) {
return;
}
if (prevDc != null && prevDc.mTokenMap.remove(token.token) != null
&& token.asAppWindowToken() == null) {
// Removed the token from the map, but made sure it's not an app token before removing
// from parent.
token.getParent().removeChild(token);
}
addWindowToken(token.token, token);
}
此時一旦發(fā)現,如果這個WindowToken已經綁過了并且還呆在這個這個目標DisplayContent就沒有必要繼續(xù)添加皂吮。否則將會判斷當前WindowToken是否已經綁定了父WindowContainer戒傻,把它從父WindowContainer移除出來。
接下來就是addWindowToken的邏輯蜂筹。
private void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
// this moving forward we will either need to merge the WindowTokens some how or have
// the binder map to a list of window tokens.
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
}
if (binder == null) {
throw new IllegalArgumentException("Can't map token=" + token + " to display="
+ getName() + " binder is null");
}
if (token == null) {
throw new IllegalArgumentException("Can't map null token to display="
+ getName() + " binder=" + binder);
}
mTokenMap.put(binder, token);
if (token.asAppWindowToken() == null) {
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
case TYPE_WALLPAPER:
mBelowAppWindowsContainers.addChild(token);
break;
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
default:
mAboveAppWindowsContainers.addChild(token);
break;
}
}
}
首先會把所有的WindowToken添加到TokenMap中。這個數據結構很重要艺挪,后文會繼續(xù)聊慌盯。
如果判斷到這個WindowToken不是Activity對應的WindowToken根據WindowToken傳進來的windowType來判斷孵睬,如果是壁紙則添加到mBelowAppWindowsContainers支示,如果是輸入法彈窗則添加到mImeWindowsContainers,剩下的如StatusBar添加到mAboveAppWindowsContainers移盆。
此時的操作悼院,是把所有游離的彈窗都收集到DisplayContent。畢竟無論是StatusBar咒循,輸入法還是壁紙都能夠脫離Activity存在的据途。
而Activity對應的Window窗體又是什么時候添加的呢?這里先留給懸念叙甸。
AppWindowToken
從上面的WindowToken颖医,我們能發(fā)現這么一個子類AppWindowToken。它實際上是專門指代Activity的Window裆蒸。從源碼的角度看來熔萧,就是指PhoneWindow對應到WMS的句柄對象。
我們還是粗略看看其構造函數
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
int configChanges, boolean launchTaskBehind, boolean alwaysFocusable,
AppWindowContainerController controller) {
this(service, token, voiceInteraction, dc, fullscreen);
setController(controller);
mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
mShowForAllUsers = showForAllUsers;
mTargetSdk = targetSdk;
mOrientation = orientation;
layoutConfigChanges = (configChanges & (CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION)) != 0;
mLaunchTaskBehind = launchTaskBehind;
mAlwaysFocusable = alwaysFocusable;
mRotationAnimationHint = rotationAnimationHint;
// Application tokens start out hidden.
setHidden(true);
hiddenRequested = true;
}
持有了一個IApplicationToken,判斷該窗口隸屬于哪個應用程序佛致。此時傳入了AppWindowContainerController 一個WindowConatinerController贮缕。這個將會控制著這個AppWindowToken。
當然這里需要和ActivityRecord區(qū)分對待俺榆,ActivityRecord是Acivity對應在AMS中的實例感昼。而AppWindowToken是Activity窗體對應在WMS中的實例。
到了后面我們就能看到實際上ActivityRecord和AppWindowToken都通過一個IApplicationToken的Binder對象維系起來罐脊。
此時AppWindowToken會對應一個AppWindowContainerController 定嗓。
WindowState
WindowState實際上是WMS用來控制每一個Window的狀態(tài)。里面包含了復雜的邏輯如計算當前窗體的大小萍桌,如控制Session的綁定等宵溅。詳細的不展開,將會在后文聊到上炎。
Task
Task這個名詞讓我們聯想到Activity對應的棧恃逻。那么這個和TaskRecord又有什么區(qū)別呢?實際上我們我們仔細看Task的繼承關系,Task是繼承于WindowConatainer藕施,確定了內部的泛型為AppWindowToken辛块。
class Task extends WindowContainer<AppWindowToken>
可以說,其本質上就是為了控制應用端Activity窗口對應的AppWindowToken的List集合铅碍。除此之外還包含了計算當前WindowContainer的邊緣润绵,大小,位置等胞谈。
讓我們看看其構造函數:
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
boolean supportsPictureInPicture, TaskDescription taskDescription,
TaskWindowContainerController controller) {
super(service);
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
setController(controller);
setBounds(getOverrideBounds());
mTaskDescription = taskDescription;
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
}
里面包含了當前Task的id尘盼。當前Task處于哪一個Task棧中,ActivityInfo對應的resizeMode烦绳,Task描述等等卿捎。
這里的Task和TaskRecord的關系其實就和AppWindowToken和ActivityRecord的關系一樣【睹埽可以抽象的看成同一種對象在兩種不同的服務的表現形式午阵。
而TaskRecord和Task又是通過什么維系起來的呢?我們能夠從構造函數中了解到享扔,此時的Task和TaskRecord都有一個TaskId把兩者聯系起來底桂。
TaskStack
TaskStack,從名字上看來惧眠,是一個Task的管理棧籽懦。從實現的角度來看其確定了泛型是Task。說明這是WMS管理Task的一個數據結構氛魁。當然每一個Stack都有自己的Id作為唯一的標識暮顺。
看看構造函數:
TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
super(service);
mStackId = stackId;
setController(controller);
mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_minimize_thickness);
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
}
能夠看到此時TaskStack又會對應一個StackId厅篓,對應一個Window容器控制者,StackWindowController 捶码。
WindowContainerController
作為WindowContainer的控制者羽氮。實際上其應用場景在Activity啟動中十分廣泛。
從上面的WindowConatiner惫恼,我們能夠知道每一個WindowContainerController和WindowContainer都有如下的控制關系乏苦。
為了更加深刻的理解這幾個數據結構,讓我們也稍微過一下這些控制者們尤筐。
首先來看看所有窗體容器控制者的父類,WindowContainerController.
WindowContainerController
這個類很簡單洞就,直接放出整個類出來:
class WindowContainerController<E extends WindowContainer, I extends WindowContainerListener>
implements ConfigurationContainerListener {
final WindowManagerService mService;
final RootWindowContainer mRoot;
final WindowHashMap mWindowMap;
// The window container this controller owns.
E mContainer;
// Interface for communicating changes back to the owner.
final I mListener;
WindowContainerController(I listener, WindowManagerService service) {
mListener = listener;
mService = service;
mRoot = mService != null ? mService.mRoot : null;
mWindowMap = mService != null ? mService.mWindowMap : null;
}
void setContainer(E container) {
if (mContainer != null && container != null) {
throw new IllegalArgumentException("Can't set container=" + container
+ " for controller=" + this + " Already set to=" + mContainer);
}
mContainer = container;
if (mContainer != null && mListener != null) {
mListener.registerConfigurationChangeListener(this);
}
}
void removeContainer() {
// TODO: See if most uses cases should support removeIfPossible here.
//mContainer.removeIfPossible();
if (mContainer == null) {
return;
}
mContainer.setController(null);
mContainer = null;
if (mListener != null) {
mListener.unregisterConfigurationChangeListener(this);
}
}
@Override
public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
synchronized (mWindowMap) {
if (mContainer == null) {
return;
}
mContainer.onOverrideConfigurationChanged(overrideConfiguration);
}
}
}
從這個Controller盆繁,我們能夠了解到,他的父類在構造函數的時候旬蟋,會把RootWindowContainer傳進來油昂。并且傳入mWindowMap這個Map數據結構。記住這個mWindowMap數據結構倾贰,它的重要性和上面的mTokenMap一樣冕碟。
接著就能知道Controller每一次只會控制一個WindowContainer,只會監(jiān)聽當前WindowConatainer回調的onOverrideConfigurationChanged匆浙。
AppWindowContainerController
從類的繼承關系看來:
public class AppWindowContainerController
extends WindowContainerController<AppWindowToken, AppWindowContainerListener>
能從確定的泛型看到這個控制者安寺,從父類角度來看,監(jiān)聽的AppWindowToken中回調AppWindowContainerListener首尼。同時控制著AppWindowToken挑庶。究竟怎么控制,看看構造函數就清楚了软能。
public AppWindowContainerController(TaskWindowContainerController taskController,
IApplicationToken token, AppWindowContainerListener listener, int index,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
WindowManagerService service) {
super(listener, service);
mHandler = new H(service.mH.getLooper());
mToken = token;
synchronized(mWindowMap) {
AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
if (atoken != null) {
// TODO: Should this throw an exception instead?
Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
return;
}
final Task task = taskController.mContainer;
if (task == null) {
throw new IllegalArgumentException("AppWindowContainerController: invalid "
+ " controller=" + taskController);
}
atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
alwaysFocusable, this);
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
+ " controller=" + taskController + " at " + index);
task.addChild(atoken, index);
}
}
從構造函數中就能看到如下邏輯:
- 1.首先會根據ActivityRecord中的IApplicationToken去RootWindContainer中查找有沒有對應的AppWindowToken.如果這個窗口本身存在迎捺,那么必定會存在,不存在說明Activity在創(chuàng)建查排,需要調用createAppWindow 方法創(chuàng)建一個AppWindowToken對應上來凳枝。
但是AppWindowToken和ActivityRecord一樣,數據結構上必須相似跋核,也就說會跟著Task岖瑰。如果找不到Task,說明此時是非法砂代。找到則會通過Task的addChlid的方法添加到從構造函數傳下來的位置锭环。
TaskWindowContainerController
相似的,看看Task的窗體管理者的構造函數:
public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
StackWindowController stackController, int userId, Rect bounds, int resizeMode,
boolean supportsPictureInPicture, boolean toTop, boolean showForAllUsers,
TaskDescription taskDescription, WindowManagerService service) {
super(listener, service);
mTaskId = taskId;
mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
synchronized(mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
+ " stack=" + stackController + " bounds=" + bounds);
final TaskStack stack = stackController.mContainer;
if (stack == null) {
throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
+ stackController);
}
EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
final Task task = createTask(taskId, stack, userId, resizeMode,
supportsPictureInPicture, taskDescription);
final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
// We only want to move the parents to the parents if we are creating this task at the
// top of its stack.
stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
}
}
其邏輯和AppWindowContainerController很相似泊藕,每一個Task中都會添加到一個TaskStack中辅辩。Task的生成將會由TaskWindowContainerController的createTask控制难礼。
StackWindowController
同樣的,看看StackWindowController 作為最頂層的數據結構玫锋,窗體又是怎么控制的蛾茉。
public StackWindowController(int stackId, StackWindowListener listener,
int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
super(listener, service);
mStackId = stackId;
mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
synchronized (mWindowMap) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
throw new IllegalArgumentException("Trying to add stackId=" + stackId
+ " to unknown displayId=" + displayId);
}
dc.createStack(stackId, onTop, this);
getRawBounds(outBounds);
}
}
能看到的是,只要調用了StackWindowController構造函數撩鹿,就必定根據當前的傳入的stackId谦炬,嘗試著通過DisplayContent創(chuàng)建一個Stack出來。
因此我們看看DisplayContent中的方法节沦。
DisplayContent.createStack
TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
final TaskStack stack = new TaskStack(mService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
return stack;
}
此時就能看到了键思,這個時候將會生成一個TaskStack的對象。不過令我吃驚的是甫贯,居然沒有對同一個StackId的情況做處理吼鳞。這個地方雖然不太可能出現相同id的TaskStack,不過感覺還是不夠嚴謹叫搁。
還記得我上面在addWindowToken中說過的話嗎赔桌?addWindowToken只是處理游離在Activity外面的Window,如壁紙和輸入法彈窗渴逻。而Activity相關的Window實際上是借由Stack生成的時候疾党,把TaskStack這個WindowContainer添加到DisplayContent的內部類TaskStackContainers這個WindowContainer中。
實際上TaskStackContainers源碼上的注釋并不是很準確惨奕,實際上管理不光光只是Activity雪位,而是管理著TaskStack這個總管理者。
我們稍微看看TaskStackContainers這個類梨撞。
TaskStackContainers
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack>
我們能夠看到DisplayContent內部類TaskStackContainers 也是繼承了DisplayChildWindowContainer茧泪。換句話說,實際上包含了上面的WindowContainer控制子WindowContainer的操作聋袋。
void addStackToDisplay(TaskStack stack, boolean onTop) {
addStackReferenceIfNeeded(stack);
addChild(stack, onTop);
stack.onDisplayChanged(DisplayContent.this);
}
在添加的時候队伟,我們看看addStackReferenceIfNeeded方法:
private void addStackReferenceIfNeeded(TaskStack stack) {
if (stack.isActivityTypeHome()) {
if (mHomeStack != null) {
throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+ mHomeStack + " already exist on display=" + this + " stack=" + stack);
}
mHomeStack = stack;
}
final int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
if (mPinnedStack != null) {
throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
+ mPinnedStack + " already exist on display=" + this
+ " stack=" + stack);
}
mPinnedStack = stack;
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
if (mSplitScreenPrimaryStack != null) {
throw new IllegalArgumentException("addStackReferenceIfNeeded:"
+ " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
能夠很輕易的發(fā)現,此時在TaskStackContainers 中會控制著當前邏輯屏幕主要的Stack幽勒,如Home嗜侮,分屏等。這樣就佐證了啥容,實際上DisplayContent確實代表著邏輯顯示屏幕锈颗。如果不是分屏的情況下,則會只有一個DisplayContent咪惠,也只有一個TaskStackContainers 击吱。這個TaskContainer會控制著應用各個窗口的棧。
總結
根據上面WindowContainer之間的關系遥昧,WindowContainerController和WindowContainer之間的關系覆醇,我們能夠構造出下面這個關系朵纷。
先來看看WindowContainer之間的關系:
因為WMS的窗體管理體系在Android9.0比起Android4.4,7.0來說抽象出了不少的對象,如果事先沒有先對這些對象有一定了解永脓,直接沖到源碼里面閱讀一定會暈頭轉向袍辞。
如果僅僅只是閱讀我的Activity的啟動流程一文,一定會感覺到意猶未盡常摧,甚至越來越糊涂搅吁,因為上一個系列并沒有涉及到窗口相關的內容。
相信就算是看到這些WindowContainer的工作原理落午,大體上已經對WMS的工作有一點了解谎懦。只剩下把這些線索串起來,下一篇文章將會走一遍核心流程溃斋,看看WMS究竟是怎么在Activity啟動流程中增加Window界拦,把這些線索串起來,把Activity的啟動流程串起來盐类,相信會有不一樣的理解。
后話
實際上呛谜,我閱讀Android的顯示體系已經花了挺久時間的在跳,閱讀到了底層更是需要對OpenGL es有一定的了解。一直沒有多少把握寫好Android顯示體系的文章隐岛。因為涉及面實在太多了猫妙,有時候一個點看不太懂一看就是一個星期。WMS相對底層來說聚凹,就顯得十分的可愛割坠,沒有c/c++那樣的艱澀難懂。
當然妒牙,如果你對OpenGL es沒有多少了解也沒關系彼哼,可以跟著我寫的OpenGL學習筆記一起學習一些基本的OpenGL,相信你會有不少收獲湘今。