a. display & window之显示
window对应用层来说来,是view跟display解耦,对framework来说,是用来display跟layer(即surfaceflinger)解耦,简单的理解:display ->layer/view<-window。
从显示的角度来看,并没有windows这个概念,只有layer图层,display显示屏的概念,window提供了一个桥梁来连接display和layer,因为安卓系统只允许应用处理自己的view关系,而且display并不属于应用可以管理的范围,于是就在activity的生命周期加了入window来桥接displayt和view,window决定了view的显示,具体window和display创建的流程如下:
一、window创建流程
如上图,应用进程activity的生命周期函数里面按步骤创建window,view,关联view和window,会binder调到WMS的addwindow。
注:此处的WindowSession.addToDisplay 应为**com.android.server.wm**.Session
WMS, WindowToken 里的displaycontent即是Display。
addToDisplay 里会执行 WindowManagerService的addwindow。
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
UserHandle.getUserId(mUid), requestedVisibilities, outInputChannel, outInsetsState,
outActiveControls);
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState outInsetsState) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
UserHandle.getUserId(mUid), mDummyRequestedVisibilities, null /* outInputChannel */,
outInsetsState, mDummyControls);
}
在addwindow中对应不同场景。
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
int userId = UserHandle.getUserId(session.mUid);
if (requestUserId != userId) {
try {
mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,
false /*allowAll*/, ALLOW_NON_FULL, null, null);
} catch (Exception exp) {
ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",
requestUserId);
return WindowManagerGlobal.ADD_INVALID_USER;
}
// It's fine to use this userId
userId = requestUserId;
}
ViewRootImpl中requestLayout函数调用mWindowSession.relayout,最终调用WindowManagerservice 的relayoutWindow,这个函数会配置Display信息; WindowStateAnimator创建Surfacecontrol,通过SurfaceControl,设置Layerstack。即在触发view更新绘制的时候才会更新图层数据。
// com.android.server.wm.WindowManagerService#relayoutWindow
if (shouldRelayout) {
try {
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
ProtoLog.w(WM_ERROR,
"Exception thrown when creating surface for client %s (%s). %s",
client, win.mAttrs.getTitle(), e);
Binder.restoreCallingIdentity(origId);
return 0;
}
}
至此,一个Windows对应一个ViewRootImpl,用来管理应用进程的view的绘制生命周期,事件机制等等,后续的流程是layer会在surfaceflinger里面处理后,经由GPU或者硬件合成渲染。
二、display创建流程
display主要由DisplayManagerService管理,DisplayManagerService是负责Display管理的系统服务之一,还有一些其他功能,包括屏幕亮度调节也涉及到DMS,其继承自SystemService,因此具有SystemService子类的共性:具有生命周期方法,由SystemServer启动、注册到系统服务中,通过Binder和其他组件进行交互等。
Display类的类型有四种:LocalDisplay、OverlayDisplay、WifiDisplay和VirtualDisplay。每一个Display对应一个DisplayAdapter。
LocalDisplayAdapter:本地已经存在的物理显示屏设备。
OverlayDisplayAdapter:模拟辅助显示设备,以类似浮动窗口的形式显示在主屏上,可以当第二个屏幕使用,默认也是镜像主屏。
WifiDisplayAdapter:WiFi Display
VirtualDisplayAdapter:显示一个虚拟屏幕
不同的类型的display的创建过程大同小异,在DMS里面的数据结构是DisplayDevice,不同类型的DisplayDevice都是由不同类型的DisplayAdapter创建的。虚拟屏mVirtualDisplayAdapter并没有在这里创建,在后面调用到的地方再创建。
以VirtualDisplayDevice的创建为例来说明一个display创建过程,不同的安卓版本这部分代码有不少变动,以sdK-31为例:
应用进程调用到DisplayManagerGlobal,全局只有这一个,在里面提供了获取创建VirtualDisplay的方法。
获取所有逻辑id的方法getDisplayIds,和根据id返回info的方法,都是IPC调用到DisplayManagerService返回数据,可以遍历disaplyinfo。
VirtualDisplayDevice创建流程如下:
经过如下三个函数的调用就创建了VirtualDisplayDevice,但是这时候只是创建好VirtualDisplay。
三、VirtualDisplayDevice的显示内容
创建后需要对DisplayDevices遍历处理,更新,同时也会加入到LogicDisplay。
遍历过程会对Device进行映射。默认是映射到Display.DEFAULT_DISPLAY即物理主屏。
映射完成后会调用LogicDisplayDevice.configureDisplayLocked对显示的内容layer进行传递。
此函数注释如下,意思是这个device可以不显示成主屏的内容,要看被你镜像的device是谁:
也就是这段代码device.getDisplayIdToMirrorLocked():
在com.android.server.display.VirtualDisplayAdapter.VirtualDisplayDevice 里面这getDisplayIdToMirrorLocked被重写,这里可以指定自己想要镜像的ID,0,2,3...(已验证),可以实现的功能是,录像,截屏,分享等可以拿取到指定的屏幕。
这个mDisplayIdToMirror是在VirtualDisplayConfig里面的成员变量。
VirtualDisplayConfig的建造函数提供了setDisplayIdToMirror,此处是否可以实现指定被镜像的display待验证。
下一步可以分析,创建后正在绘制的display,动态更新到不同的指定屏上...
参考资料
[Android Display管理服务DMS]: https://www.jianshu.com/p/c2058c2dca95
[Android VirtualDisplay解析]: https://www.jianshu.com/p/476a357ed363