本文转载自:
- Android R WindowManagerService模块(2) Window的添加过程
- Android解析WindowManager(三)Window的添加过程
本文基于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操作,从而开始执行布局过程。后面文章将对布局的过程进行分析。