Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析.docx
- 文档编号:17633066
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:43
- 大小:319.19KB
Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析.docx
《Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析.docx》由会员分享,可在线阅读,更多相关《Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析.docx(43页珍藏版)》请在冰点文库上搜索。
Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析
Android窗口管理服务WindowManagerService计算窗口Z轴位置的过程分析
通过前面几篇文章的学习,我们知道了在Android系统中,无论是普通的Activity窗口,还是特殊的输入法窗口和壁纸窗口,它们都是被WindowManagerService服务组织在一个窗口堆栈中的,其中,Z轴位置较大的窗口排列在Z轴位置较小的窗口的上面。
有了这个窗口堆栈之后,WindowManagerService服务就可以按照一定的规则计算每一个窗口的Z轴位置了,本文就详细分析这个计算过程。
基于窗口堆栈来计算窗口的Z轴位置是比较有意思的。
按照一般的理解,应该是先计算好窗口的Z轴位置,然后再按照Z轴位置的大小来将各个窗口排列在堆栈中。
但是,事实上,窗口是按照其它规则排列在堆栈中。
这些规则与窗口的类型、创建顺序和运行状态等有关。
例如,状态栏窗口总是位于堆栈的顶端,输入法窗口总是位于需要输入法的窗口的上面,而壁纸窗口总是位于需要显示壁纸的窗口的下面。
又如,当一个Activity组件从后台激活到前台时,与它所对应的窗口就会被相应地移动到窗口堆栈的上面去。
从前面和这两个系列的文章可以知道,窗口的UI最终是需要通过SurfaceFlinger服务来统一渲染的,而SurfaceFlinger服务在渲染窗口的UI之前,需要计算基于各个窗口的Z轴位置来计算它们的可见区域。
因此,WindowManagerService服务计算好每一个窗口的Z轴位置之后,还需要将它们设置到SurfaceFlinger服务中去,以便SurfaceFlinger服务可以正确地渲染每一个窗口的UI。
上述窗口的Z轴位置计算和设置过程如图1所示:
接下来,我们就首先分析两个需要重新计算窗口Z轴位置的情景,接着再分析窗口的Z轴位置的计算过程,最后分析WindowManagerService服务将窗口的Z轴设置到SurfaceFlinger服务中去的过程。
一.需要重新计算窗口Z轴位置的情景
这里主要分析两个需要重新计算窗口Z轴位置的情景:
应用程序增加一个窗口到WindowManagerService服务和应用程序请求WindowManagerService服务重新布局一个窗口。
从前面一文可以知道,应用程序请求增加一个窗口到WindowManagerService服务的时候,最终会调用到WindowManagerService类的成员函数addWindow。
接下来我们就主要分析这个函数与重新计算窗口Z轴位置相关的逻辑,如下所示:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassWindowManagerServiceextendsIWindowManager.Stub
implementsWatchdog.Monitor{
......
publicintaddWindow(Sessionsession,IWindowclient,
WindowManager.LayoutParamsattrs,intviewVisibility,
RectoutContentInsets,InputChanneloutInputChannel){
......
synchronized(mWindowMap){
......
WindowTokentoken=mTokenMap.get(attrs.token);
......
win=newWindowState(session,client,token,
attachedWindow,attrs,viewVisibility);
......
if(attrs.type==TYPE_INPUT_METHOD){
mInputMethodWindow=win;
addInputMethodWindowToListLocked(win);
......
}elseif(attrs.type==TYPE_INPUT_METHOD_DIALOG){
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win,true);
adjustInputMethodDialogsLocked();
......
}else{
addWindowToListInOrderLocked(win,true);
if(attrs.type==TYPE_WALLPAPER){
......
adjustWallpaperWindowsLocked();
}elseif((attrs.flags&FLAG_SHOW_WALLPAPER)!
=0){
adjustWallpaperWindowsLocked();
}
}
......
assignLayersLocked();
......
}
......
}
......
}
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService类的成员函数addWindow的具体实现可以参考和这两篇文章。
我们注意到,WindowManagerService类的成员函数addWindow会根据当前正在添加的窗口的类型来调用不同的成员函数来向窗口堆栈的合适位置插入一个WindowState对象,即:
1.如果添加的是一个输入法窗口,那么就调用成员函数addInputMethodWindowToListLocked将它放置在需要显示输入法的窗口的上面去;
2.如果添加的是一个输入法对话框,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustInputMethodDialogsLocked来将它放置在输入法窗口的上面;
3.如果添加的是一个普通窗口,那么就直接调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中;
4.如果添加的是一个普通窗口,并且这个窗口需要显示壁纸,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustWallpaperWindowsLocked来将壁纸窗口放置在它的下面。
5.如果添加的是一个壁纸窗口,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustWallpaperWindowsLocked来将它放置在需要显示壁纸的窗口的下面。
无论如何,WindowManagerService类的成员函数addWindow最终都会调用成员函数assignLayersLocked来重新计算系统中所有窗口的Z轴位置,这是因为前面往窗口堆栈增加了一个新的窗口。
从前面一文可以知道,应用程序进程请求WindowManagerService服务重新布局一个窗口的时候,最终会调用到WindowManagerService类的成员函数relayoutWindow。
接下来我们就主要分析这个函数与重新计算窗口Z轴位置相关的逻辑,如下所示:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassWindowManagerServiceextendsIWindowManager.Stub
implementsWatchdog.Monitor{
......
publicintrelayoutWindow(Sessionsession,IWindowclient,
WindowManager.LayoutParamsattrs,intrequestedWidth,
intrequestedHeight,intviewVisibility,booleaninsetsPending,
RectoutFrame,RectoutContentInsets,RectoutVisibleInsets,
ConfigurationoutConfig,SurfaceoutSurface){
......
synchronized(mWindowMap){
WindowStatewin=windowForClientLocked(session,client,false);
......
intattrChanges=0;
intflagChanges=0;
if(attrs!
=null){
flagChanges=win.mAttrs.flags^=attrs.flags;
attrChanges=win.mAttrs.copyFrom(attrs);
}
......
booleanimMayMove=(flagChanges&(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE))!
=0;
booleanfocusMayChange=win.mViewVisibility!
=viewVisibility
||((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)!
=0)
||(!
win.mRelayoutCalled);
booleanwallpaperMayMove=win.mViewVisibility!
=viewVisibility
&&(win.mAttrs.flags&FLAG_SHOW_WALLPAPER)!
=0;
......
if(focusMayChange){
......
if(updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)){
imMayMove=false;
}
......
}
//updateFocusedWindowLocked()alreadyassignedlayerssoweonlyneedto
//reassignthematthispointiftheIMwindowstategetsshuffled
booleanassignLayers=false;
if(imMayMove){
if(moveInputMethodWindowsIfNeededLocked(false)||displayed){
//Littlehackhere--we-should-beabletorelyonthe
//functiontoreturntrueiftheIMEhasmovedandneeds
//itslayerrecomputed.However,iftheIMEwashidden
//andisn'tactuallymovedinthelist,itslayermaybe
//outofdatasowemakesuretorecomputeit.
assignLayers=true;
}
}
if(wallpaperMayMove){
if((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED)!
=0){
assignLayers=true;
}
}
......
if(assignLayers){
assignLayersLocked();
}
......
}
......
return(inTouchMode?
WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE:
0)
|(displayed?
WindowManagerImpl.RELAYOUT_FIRST_TIME:
0);
}
......
}
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowManagerService类的成员函数relayoutWindow具体实现可以参考和这两篇文章,与窗口Z轴位置计算相关的逻辑大概是这样的:
1.如果系统当前获得焦点的窗口可能发生了变化,那么就会调用成员函数updateFocusedWindowLocked来重新计算系统当前应该获得焦点的窗口。
如果系统当前获得焦点的窗口真的发生了变化,即窗口堆栈的窗口排列发生了变化,那么在调用成员函数updateFocusedWindowLocked的时候,就会调用成员函数assignLayersLocked来重新计算系统中所有窗口的Z轴位置。
2.如果系统中的输入法窗口可能需要移动,那么就会调用成员函数moveInputMethodWindowsIfNeededLocked来检查是否真的需要移动输入法窗口。
如果需要移动,那么成员函数moveInputMethodWindowsIfNeededLocked的返回值就会等于true,这时候就说明输入法窗口在窗口堆栈中的位置发生了变化,因此,就会将变量assignLayers的值设置为true,表示接下来需要重新计算系统中所有窗口的Z轴位置。
3.如果当前正在请求调整其布局的窗口是由不可见变化可见的,即变量displayed的值等于true,那么接下来也是需要重新计算系统中所有窗口的Z轴位置的,因此,就会将assignLayers的值设置为true。
4.如果系统中的壁纸窗口可能需要移动,那么就会调用成员函数adjustWallpaperWindowsLocked来检查是否真的需要移动壁纸窗口。
如果需要移动,那么成员函数adjustWallpaperWindowsLocked的返回值的ADJUST_WALLPAPER_LAYERS_CHANGED位就会等于1,这时候就说明壁纸窗口在窗口堆栈中的位置发生了变化,因此,就会将变量assignLayers的值设置为true,表示接下来需要重新计算系统中所有窗口的Z轴位置。
经过上述的一系列操作后,如果得到的变量assignLayers的值设置等于true,那么WindowManagerService类的成员函数relayoutWindow就会调用成员函数assignLayersLocked来重新计算系统中所有窗口的Z轴位置。
二.计算系统中所有窗口的Z轴位置
从前面第一部分的内容可以知道,一旦窗口堆栈中的窗口发生了变化,那么WindowManagerService类的成员函数assignLayersLocked就会调用来计算系统中所有窗口的Z轴位置。
窗口的Z轴位置除了与它在窗口堆栈中的位置有关之外,还与窗口的类型有关。
窗口的类型在创建的时候就已经是确定了的,WindowManagerService服务在为它创建一个WindowState对象的时候,就会根据它的类型得到一个BaseLayer值,这个BaseLayer值在计算它的Z轴位置的时候会用到。
接下来我们就通过WindowState类的构造函数来分析一个窗口的BaseLayer值是如何确定的,如下所示:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassWindowManagerServiceextendsIWindowManager.Stub
implementsWatchdog.Monitor{
......
/**Howmuchtomultiplythepolicy'stypelayer,toreserveroom
*formultiplewindowsofthesametypeandZ-orderingadjustment
*withTYPE_LAYER_OFFSET.*/
staticfinalintTYPE_LAYER_MULTIPLIER=10000;
/**OffsetfromTYPE_LAYER_MULTIPLIERformovingagroupofwindowsabove
*orbelowothersinthesamelayer.*/
staticfinalintTYPE_LAYER_OFFSET=1000;
......
privatefinalclassWindowStateimplementsWindowManagerPolicy.WindowState{
......
finalintmBaseLayer;
finalintmSubLayer;
......
WindowState(Sessions,IWindowc,WindowTokentoken,
WindowStateattachedWindow,WindowManager.LayoutParamsa,
intviewVisibility){
......
if((mAttrs.type>=FIRST_SUB_WINDOW&&
mAttrs.type<=LAST_SUB_WINDOW)){
//Themultiplierhereistoreservespaceformultiple
//windowsinthesametypelayer.
mBaseLayer=mPolicy.windowTypeToLayerLw(
attachedWindow.mAttrs.type)*TYPE_LAYER_MULTIPLIER
+TYPE_LAYER_OFFSET;
mSubLayer=mPolicy.subWindowTypeToLayerLw(a.type);
......
}else{
//Themultiplierhereistoreservespaceformultiple
//windowsinthesametypelayer.
mBaseLayer=mPolicy.windowTypeToLayerLw(a.type)
*TYPE_LAYER_MULTIPLIER
+TYPE_LAYER_OFFSET;
mSubLayer=0;
......
}
......
}
......
}
......
}
这个函数定义在文件frameworks/base/services/Java/com/android/server/WindowManagerService.java中。
一个窗口除了有一个BaseLayer值之外,还有一个SubLayer值,分别保存在一个对应的WindowState对象的成员变量mBaseLayer和mSubLayer。
SubLayer值是用来描述一个窗口是否是另外一个窗口的子窗口的。
假设一个窗口是另外一个窗口的子窗口,那么参数attachedWindow所描述的窗口就是父窗口,这时候子窗口的BaseLayer值就等于父窗口的BaseLayer值,而SubLayer值要么大于0,要么小于0,这与它自己的具体类型有关。
假设一个窗口不是另外一个窗口的子窗口,那么这个窗口的BaseLayer值就与它自己的具体类型有关,而SubLayer值就等于0。
现在的关键就是要根据窗口的类型来计算它的BaseLayer值和SubLayer值,它们分别是通过调用WindowManagerService类的成员变量mPolicy所指向的一个PhoneWindowManager对象的成员函数windowTypeToLayerLw和subWindowTypeToLayerLw来计算得到的。
这里有两个地方是需要注意的。
第一个地方是PhoneWindowManager对象的成员函数windowTypeToLayerLw的返回值并且不是一个窗口的最终的BaseLayer值,而是要将它的返回值乘以一个常量TYPE_LAYER_MULTIPLIER,再加上另外一个常量TYPE_LAYER_OFFSET之后,才得到最终的BaseLayer值。
这是因为在Android系统中,相同类型的窗口的Z轴位置都是有着相同的值域,而不同类型的窗口的Z轴位置都是处于两个不相交的值域。
例如,假设有两种不同类型的窗口,它们的Z轴位置的值域分别为[a,b]和[c,d],那么[a,b]和[c,d]的交集一定等于空。
又由于每一种类型的窗口的数量是不确定的,因此,WindowManagerService服务就需要为每一种类型的窗口都预留一个范围足够大的值域,以便可以满足要求。
WindowManagerService服务是如何为类型相同的窗口的Z轴位置预留一个范围足够大的值域的呢?
我们假设类型为t的窗口的Z轴位置的值域为[a,b],并且以t为参数调用PhoneWindowManager对象的成员函数windowTypeToLayerLw的返回值为T,那么a的值就等于T*TYPE_LAYER_MULTIPLIER+TYPE_LAYER_OFFSET,而b的值就等于(T-1)*TYPE_LAYER_MULTIPLIER+TYPE_LAYER_OFFSET-1,即从T*TYPE_LAYER_MULTIPLIER+TYPE_LAYER_OFFSET开始,一共预留了TYPE_LAYER_MULTIPLIER个值作为类型为t窗口的Z轴位置。
由于TYPE_LAYER_MULTIPLIER的值定义为10000,而TYPE_LAYER_OFFSET的值定义
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 窗口 管理 服务 WindowManagerService 计算 位置 过程 分析
链接地址:https://www.bingdoc.com/p-17633066.html