本文轉載自:
本文基于Android 11.0源碼分析
前言
??窗口的添加,站在用戶角度看,是開啟一個新界面;站在開發者角度看,是通過API創建了一個新的Activity或窗口;站在系統實現角度看,則并非如此簡單,本篇文章的目的,就是弄明白,當添加一個窗口時,系統相關管理者做了哪些工作。
1.添加窗口API
??WindowManager是Android中提供給APP用來創建窗口的。在結構上,WindowManager繼承于ViewManager,從API看,只提供了三個接口用來給APP添加或移除window:
abstract void addView(View view, ViewGroup.LayoutParams params)
abstract void removeView(View view)
abstract void updateViewLayout(View view, ViewGroup.LayoutParams params)
上面方法中也可以知道,在創建窗口時,我們只需要考慮這個窗口要展示的View、以及窗口的屬性(如大小、類型,全部放置在WindowManager.LayoutParams中)。
??WindowManager.LayoutParams是ViewGroup.LayoutParams的子類,該類用來攜帶創建Window時所設置的所有屬性,如Window類型、屬性、格式、Flag等。
1.1 創建基本窗口
??創建一個Window,只需要如下幾步即可:
private void addWindow() {
// 1.獲取WindowManager對象
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
// 2.獲取Window.LayoutParams對象
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
// 3.定義Window要展示的View
LinearLayout linearLayout = (LinearLayout) View.inflate(this,R.layout.window_layout,null);
// 4.添加View
wm.addView(linearLayout, layoutParams);
}
以上示例第2步中,創建WindowManager.LayoutParams對象時使用了默認構造函數,除此之外,WindowManager API提供了7個構造方法用來創建WindowManager.LayoutParams:
//frameworks/base/core/java/android/view/WindowManager.java
WindowManager.LayoutParams()
WindowManager.LayoutParams(int _type)
WindowManager.LayoutParams(int _type, int _flags)
WindowManager.LayoutParams(int _type, int _flags, int _format)
WindowManager.LayoutParams(int w, int h, int _type, int _flags, int _format)
WindowManager.LayoutParams(int w, int h, int xpos, int ypos, int _type, int _flags, int _format)
WindowManager.LayoutParams(Parcel in)
這些參數表示:
-
_type: 窗口類型,用來計算層級,Android中定義了三大類、20幾小類Window類型:
Application Window:[FIRST_APPLICATION_WINDOW,LAST_APPLICATION_WINDOW],[1,99];
Sub Window: [FIRST_SUB_WINDOW, LAST_SUB_WINDOW],[1000,1999];
SystemWindows: [FIRST_SYSTEM_WINDOW, LAST_SYSTEM_WINDOW ], [2000,2999];
_flag: 窗口的標記,會根據攜帶的標記值設置不同的功能;
_format: Bitmap的格式,PixelFormat中定義的常量;
xpos/ypos: Window的x、y坐標。
??接下來就從WindowManager.addView()這個方法開始,看起Window是如何創建的。
2.Window的添加過程
??三大類窗口的添加過程會有所不同,這里以系統窗口StatusBar為例,StatusBar是SystemUI的重要組成部分,具體就是指系統狀態欄,用于顯示時間、電量和信號等信息。我們來查看StatusBar的實現類PhoneStatusBar的addStatusBarWindow方法,這個方法負責為StatusBar添加Window。下面我們從PhoneStatusBar.addStatusBarWindow()方法開始,對添加窗口過程進行分析。
2.1 PhoneStatusBar.addStatusBarWindow()
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
private void addStatusBarWindow() {
makeStatusBarView();//1.構建視圖
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//2
}
注釋1處用于構建StatusBar的視圖。在注釋2處調用了StatusBarWindowManager的add方法,并將StatusBar的視圖(StatusBarWindowView)和StatusBar的傳進去。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
public void add(View statusBarView, int barHeight) {
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,//1.窗口類型
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);//2.添加窗口
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
首先通過創建LayoutParams來配置StatusBar視圖的屬性,包括Width、Height、Type、 Flag、Gravity、SoftInputMode等。 關鍵在注釋1處,設置了TYPE_STATUS_BAR,表示StatusBar視圖的窗口類型是狀態欄。在注釋2處調用了WindowManager的addView方法,addView方法定義在WindowManager的父類接口ViewManager中,而實現addView方法的則是WindowManagerImpl中。
2.2 WindowManagerImpl.addView()
??在framework層實現中,WindowMananger是一個接口,客戶端得到的WindowMananger對象實際上是WindowManangerImpl實例,因此客戶端執行WindowManager.addView()后,將會進入到WindowManagerImpl.addView()中:
//frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
// 如果不存在父Window且存在默認Token,會將mDefaultToken設置給params.token。一般都為null
applyDefaultToken(params);
// 進入WindowManagerGlobal
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
以上方法中,又會調用進入mGlobal#addView(),mGlobal是WindowManagerGlobal的實例,它通過單例方式提供給WindowMangerImpl:
// frameworks/base/core/java/android/view/WindowManagerImpl.java
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
WindowManagerGlobal類相對于WindoweMangerImpl來說,它的操作不需要依賴任何的Context對象。這個單例也是相對于WindoweMangerImpl而言的,在Activity啟動時,系統會為每一個Activity或Window創建一個WindoweMangerImpl對象,但一個進程內,只有一個WindowManagerGlobal類實例。
2.3 WindowManagerGlobal.addView()
??WindowManagerGlobal.addView()如下:
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
// 如果存在父window,會將params調整一致,存在父Window時,wparams.token就是在此處設置
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// 設置HARDWARE_ACCELERATED標記
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
// 創建一個ViewRootImpl,表示一個Viwe層級的根View
root = new ViewRootImpl(view.getContext(), display);
// 把params設置給View
view.setLayoutParams(wparams);
mViews.add(view); // 將View添加到mViews列表中
mRoots.add(root); // 將ViewRootImpl添加到mRoots列表中
mParams.add(wparams); // 將攜帶的LayoutParam添加到mParams列表中
try {
// 將View設置給RootViewImpl
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
}
}
}
以上方法中,會創建ViewRootImpl對象,然后將view、ViewRootImpl、WindowManager.LayoutParmas對象分別添加到了對應列表中后,并執行ViewRootImpl#setView()方法。ViewRootImpl身負了很多職責:
View樹的根并管理View樹;
觸發View的測量、布局和繪制;
輸入事件的中轉站;
管理Surface;
負責與WMS進行進程間通信。
2.4 ViewRootImpl.setView()
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
......
// 注冊VSync信號監聽器,準備進行layout
requestLayout();
try {
// 通過WindowSession,將調用進入WMS中
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
} catch (RemoteException e) {
}
}
}
在ViewRootImpl#setView()方法中,會設置這個view的許多屬性,然后執行mWindowSession#addToDisplayAsUser()方法。
??mWindowSession在創建ViewRootImpl實例時獲得,下面來看下mWindowSession的獲取過程。
2.5 獲取IWindowSession對象
??ViewRootImpl是每一個View的管理者,承載著客戶端和WMS服務端的交互重任(通過IWindowSession對象和IWindow對象),繼續來看ViewRoot的構造方法:
// frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
這里看到,通過WindowManagerGlobal#getWindowSession()方法,獲得了一個IWindowSession接口的對象。ViewRootImpl本身不具有跨進程通信的能力,當它需要向WMS發出交互請求時,就是通過IWindowSession實例來進行。
??接著看下WindowManagerGlobal#getWindowSession()方法:
// frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// WMS遠端對象
IWindowManager windowManager = getWindowManagerService();
// 從WMS中獲取Session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
......
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
在這里,通過WMS打開了一個Session:
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
WMS中則直接返回了一個Session類對象。
??由于ViewRootImpl中持有了來自于WMS中的IWindowSession實例,因此在當它需要和WMS進行交互時,將直接通過該實例進行。
3.進入sysmtem_server添加流程
??客戶端進程通過Session對象,最終通過Binder調用進入了WindowManagerService中,所以接下來的流程將在system_server中執行。
3.1 Session.addToDisplay()
進入Session.addToDisplay()后,將直接調用進入WMS中:
// frameworks/base/services/core/java/com/android/server/wm/Session.java
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
3.2 WindowManagerService#addWindow()
??WMS.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,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
......
// 獲取Window type
final int type = attrs.type;
synchronized (mGlobalLock) {
......
ActivityRecord activity = null;
// 對于type為SUB_WINDOW的窗口,判斷是否存在父窗口
final boolean hasParent = parentWindow != null;
// 根據attr.token獲取WindowToken對象
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
// 如果不存在WindowToken,創建新的WindowToken
if (token == null) {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
// 如果是Application類型的窗口,則該WindowToken向下轉型為ActivityRecord
} else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
activity = token.asActivityRecord();
.........
}
// 創建WindowState對象
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// 調整win.mAttrs屬性
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
// 創建InputChanel,用于input事件的傳遞和接收
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
// 將Windowstate和IWindow添加到mWindowMap中
mWindowMap.put(client.asBinder(), win);
// 將WindowToken向下轉型
final ActivityRecord tokenActivity = token.asActivityRecord();
boolean imMayMove = true;
// 將WindowState對象添加到WindowToken中,WindowToken將作為WindowState的父容器
win.mToken.addWindow(win);
// 窗口動畫對象
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
.....
// 設置Inset
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
// 將InputMonitor#mUpdateInputWindowsNeeded屬性設置為true,表示將更新Input內部
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
// 如果能收到按鍵
if (win.canReceiveKeys()) {
// 更新焦點窗口
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
// 為該WindowState分配layer,但是此次并不會真正地為分配layer
win.getParent().assignChildLayers();
// 更新Input Window
if (focusChanged) {
displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
// 如果方向有更新,更新全局配置
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
getInsetsSourceControls(win, outActiveControls);
}
return res;
}
以上方法中,先進行了大量的驗證,如果驗證成功,則進入開始做進一步的處理。這里省略去了一些針對特殊窗口的處理,所做工作主要如下:
根據傳入的attrs.token,從DisplayContent#mTokenMap<IBinder, WindowToken>中獲取WindowToken對象,如果獲取不到,則會創建新的WindowToken;
創建WindowState對象;
將WindowState對象添加到WindowToken中,WindowToken將成為WindowState對象的父容器;
創建InputChannel,用于input事件派發;
更新焦點窗口;
更新InputWindow。
下面分別來看這幾步操作。
3.3 創建WindowToken對象
??WindowToken作為WindowState的父容器,負責管理一組Window。Window添加過程中,WindowToken的創建是視情況而定的,因為一個WindowToken管理著一組Window,如果在添加Window的過程中,已經存在合適的WindowToken,那么就不會創建,否則會創建WindowToken。
WindowToken的構造方法如下:
//frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
boolean roundedCornerOverlay, boolean fromClientToken) {
super(service);
token = _token; // IBinder對象,作為一組窗口的實際token
windowType = type; // 窗口類型
mPersistOnEmpty = persistOnEmpty; // 是否是顯式添加的WidowToken
mOwnerCanManageAppTokens = ownerCanManageAppTokens; // 是否具有MANAGE_APP_TOKENS權限
mOwnerUid = ownerUid; // owner id
mRoundedCornerOverlay = roundedCornerOverlay; // 是否覆蓋圓角
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this); // 將該對象添加到DisplayContent中
}
}
以上方法中,在對一些屬性進行初始化后,將WindowToken和對應的IBinder對象傳遞給DisplayContent,并以<IBinder, WindowToken>的形式,保存在DisplayContent#mToken中,同時將Token放置在合適的容器中:
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void addWindowToken(IBinder binder, WindowToken token) {
// 如果該WindowToken已經關聯有DisplayContent對象,則不能再關聯其他DisplayContent對象
final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
// 將WindowToken添加到DisplayContent#mTokenMap中
mTokenMap.put(binder, token);
// 對于非Activity類型的窗口,會根據Window類型添加到對應三個container中
if (token.asActivityRecord() == null) {
// WindowToken和DisplayContent關聯
token.mDisplayContent = this;
switch (token.windowType) {
// 將輸入法Window,放在mImeWindowsContainers中
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
mOverlayContainers.addChild(token);
break;
// 其他類型Window,放在mDisplayAreaPolicy
default:
mDisplayAreaPolicy.addWindow(token);
break;
}
}
}
以上方法中,mImeWindowsContainers、mOverlayContainers和mDisplayAreaPolicy將作為WindowToken的父容器對其進行管理,其中:
mImeWindowsContainers:ImeContainer類對象,作為IME窗口的父容器,管理IME窗口;
mOverlayContainers:NonAppWindowContainers類對象,作為和應用不關聯的窗口的父容器,如狀態欄等;
mDisplayAreaPolicy: DisplayAreaPolicy類對象,它本身并非容器,但該對象作為策略會持有一個合適的容器,這個容器就是DisplayArea.Token, 來管理其他類型的窗口。
??這三個容器的類結構如下:
此時,WindowToken對象創建完畢。
3.4 創建WindowState對象
??在得到WindowToken對象后,接下來將創建WindowState對象,一個WindowState代表了一個具體的Window,每添加一個Window,都會創建對應的WindowState對象。
??WindowState類的構造方法如下:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, int showUserId,
boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) {
super(service); // 執行WindowContainer(wms)
mSession = s; // Session對象
mClient = c; // 客戶端的IWindow類型對象,該對象和客戶端進行交互
mToken = token; // WindowToken對象
mActivityRecord = mToken.asActivityRecord(); // 該token如果是ActivityRecord類型,則相當于向下轉型
mAttrs.copyFrom(a); // 從傳入的attr對象復制給mAttrs對象
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility; // 窗口可見性
mPolicy = mWmService.mPolicy; // PhoneWindowManager對象
mContext = mWmService.mContext;
mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay; // 圓角窗口的特殊標記,控制圓角的方向旋轉
// 設置Binder die代理
try {
c.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
}
mDeathRecipient = deathRecipient;
// 對Sub Window類型窗口設置BaseLayer和SubLayer值
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
|| parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
} else {// 對非子窗口類型窗口設置BaseLayer和SubLayer值
......
}
// 是否為"漂浮"類窗口,因為該倆窗口需要掛在其他窗口上顯示
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
// 創建WindowStateAnimator對象
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
// 初始話寬高
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
// 創建InputWindowHandle對象
mInputWindowHandle = new InputWindowHandle(
mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
getDisplayId());
// 如果是子窗口類型窗口,需要添加到父窗口中
if (mIsChildWindow) {
parentWindow.addChild(this, sWindowSubLayerComparator);
}
// 獲取WindowProcessController對象,管理進程
mWpcForDisplayConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
? null
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
在以上方法中,對WindState類中的許多屬性進行了初始化操作,其中還創建了兩個對象——WindowStateAnimator對象和InputWindowHandle對象。
WindowStateAnimator對象用來為WindowState對象進行動畫和Surface的轉變操作,繪制過程中,動畫的狀態和Surface狀態都會由該對象來進行記錄。
InputWindowHandle對象則用于input事件的分發,通過內部的IBinder類型的token,將InputChannel和WindowState進行關聯。這倆對象的創建相對較簡單,這里就略去了。
3.5 將WindowToken設置為WindoState的父容器
??在3.2 WindowManagerService#addWindow()小節中,會通過WindowToken#addWindow()方法,將WindowState對象添加到WindowToken中,使得WindowToken成為WindowState的父容器:
//3.2 WindowManagerService#addWindow()
win.mToken.addWindow(win);
// frameworks/base/services/core/java/com/android/server/wm/WindowToken.java
void addWindow(final WindowState win) {
// 對于子窗口而言,由父窗口管理,不需要由WindowToken管理
if (win.isChildWindow()) {
return;
}
// 如果mSurfaceControl不存在創建createSurfaceControl對象
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
}
// 添加到mChildren列表中,并設置WindowStack#mParent為該WindowToken
if (!mChildren.contains(win)) {
addChild(win, mWindowComparator);
mWmService.mWindowsChanged = true;
}
}
以上方法中,通過addChild()方法,將WindowState對象添加到WindowToken#mChildren列表中,同時將WindowToken賦值給WindowState#mParent屬性。
3.6 創建InputChannel
??InputChannel用于傳輸input事件給應用,其內部通過socket的方式,將inputFlinger中的input事件讀取并傳遞給應用,在WindowState#的方式創建并注冊了InputChannel對象:
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) {
String name = getName();
// 創建一個InputChannel對
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
// 向IMS中注冊InputChannel
mWmService.mInputManager.registerInputChannel(mInputChannel);
mInputWindowHandle.token = mInputChannel.getToken();
// 填充返回給ViewRootImpl的outInputChannel
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
}
......
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
這個方法中,首先獲取InputChannel對,然后向IMS中注冊InputChannel,最后將inputChannels[1]返回給ViewRootImpl。
3.7 更新焦點窗口
??如果該WindowState能接收key事件,則接下來會通過WMS#updateFocusedWindowLocked()方法更新全局的焦點窗口。在更新時,將會自頂向下,從RootWindowContainer → DisplayContent 對每個WindowState進行遍歷,最終獲取到一個獲得焦點的WindowState對象。
??更新焦點窗口的流程比較長,對這部分流程后續進行單獨的分析。
3.8 更新InputWindow
??“InputWindow”是指能夠接收input事件的窗口。在更新焦點窗口前,就通過InputMonitor#setUpdateInputWindowsNeededLw()方法,將InputMonitor#mUpdateInputWindowsNeeded屬性設置為true,表示接下來需要更新Input窗口。
// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
void updateInputWindowsLw(boolean force) {
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
scheduleUpdateInputWindows();
}
從Android Q開始,在SurfaceFlinger中通過Binder調用更新InputWindow給InputFlinger。
??到此為止,整個Window添加過程執行完成。整個過程時序圖如下:
4.Activity的添加窗口過程
??無論是哪種窗口,它的的添加過程在WMS處理部分中基本是類似的,只不過會在權限和窗口顯示次序等方面會有些不同。但是在WindowManager處理部分會有所不同,這里以最典型的應用程序窗口Activity為例,Activity在啟動過程中,如果Activity所在的進程不存在則會創建新的進程,創建新的進程之后就會運行代表主線程的實例ActivityThread。當界面要與用戶進行交互時,會調用ActivityThread的handleResumeActivity方法,如下所示。
//frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);//1.最終調用Activity的onResume
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();//2
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;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);//3
}
...
}
注釋1處的performResumeActivity方法最終會調用Activity的onResume方法。在注釋2處得到ViewManager類型的wm對象,在注釋3處調用了wm的addView方法,而addView方法的實現則是在WindowManagerImpl中,此后的過程在上面的系統窗口的添加過程已經講過,唯一需要注意的是addView的第一個參數是DecorView。
5.總結
添加窗口時,客戶端拿到的WindowManager對象是一個WindowManagerImpl實例,且沒個Activity和Window都各自的WindowManagerImpl實例,并且和Context綁定。相比WindowManagerImpl對象,每個進程則只有一個WindowManagerGlobal對象;
ViewRootImpl作為每個View的管理者,和WMS進行交互,兩進程交互過程通過IWindowSession和IWindow接口進行;
WMS中,WindowState對象代表一個窗口,WindowToken作為WindowState的父容器,管理著一組窗口。
??通過WMS#addWindow()方法,創建了相對于system_server的窗口管理對象,但需要顯示窗口內容的Surface還沒有創建。
??在addView()過程中,有一個requestLayout()操作,它監聽Vsync信號,并在收到Vsync信號后,請求WMS進行relayout操作,從而開始執行布局過程。后面文章將對布局的過程進行分析。