Android窗口管理服务WindowManagerService对壁纸窗口Wallpaper Window的管理分析Word下载.docx
《Android窗口管理服务WindowManagerService对壁纸窗口Wallpaper Window的管理分析Word下载.docx》由会员分享,可在线阅读,更多相关《Android窗口管理服务WindowManagerService对壁纸窗口Wallpaper Window的管理分析Word下载.docx(61页珍藏版)》请在冰点文库上搜索。
android:
theme="
@android:
style/Theme.Translucent"
>
......
/activity>
2.窗口属性中的WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER位设置为1:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassWallpaperActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
}
}
满足了以上两个条件之后,Activity窗口和壁纸窗口的位置关系就如图1所示:
在前面一文中提到,WindowManagerService服务是使用堆栈来组织系统中的窗口的,因此,如果我们在窗口堆栈中观察Activity窗口和壁纸窗口,它们的位置关系就如图2所示:
图2中的对象的关系如下所示:
1.在ActivityManagerService服务内部的Activity组件堆栈顶端的ActivityRecord对象N描述的是系统当前激活的Activity组件。
2.ActivityRecord对象N在WindowManagerService服务内部的窗口令牌列表顶端对应有一个AppWindowToken对象N。
3.AppWindowToken对象N在WindowManagerService服务内部的窗口堆栈中对应有一个WindowState对象N,用来描述系统当前激活的Activity组件窗口。
4.WindowState对象N下面有一个WindowState对象WP,用来描述系统中的壁纸窗口。
5.系统中的壁纸窗口在WindowManagerService服务内部中对应的窗口令牌是由WindowToken对象WP来描述的。
6.WindowToken对象WP在WallpaperManagerService服务中对应有一个Binder对象。
总的来说,就是图2描述了系统当前激活的Activity窗口需要显示壁纸的情景。
WindowManagerService服务的职能之一就是要时刻关注系统中是否有窗口需要显示壁纸。
WindowManagerService服务一旦发现有窗口需要显示壁纸,那么就会调整壁纸窗口在窗口堆栈中的位置,使得它放置在需要显示壁纸的窗口的下面。
此外,需要显示壁纸的窗口还可以设置壁纸窗口在X轴和Y轴上的偏移位置,以便可以将壁纸窗口的某一部分指定为它的背景。
接下来,我们就首先分析两个需要调整壁纸窗口在窗口堆栈中的位置的情景,然后再分析壁纸窗口在X轴和Y轴上的偏移位置的调整过程,最后分析壁纸窗口在窗口堆栈中的位置调整过程。
一.调整壁纸窗口在窗口堆栈中的位置的情景
第一个需要调整壁纸窗口在窗口堆栈中的位置的情景是增加一个窗口到WindowManagerService服务去的时候。
从前面一文可以知道,增加一个窗口到WindowManagerService服务最终是通过调用WindowManagerService类的成员函数addWindow来实现的。
接下来我们就主要分析这个函数中与壁纸窗口调整相关的逻辑,如下所示:
publicclassWindowManagerServiceextendsIWindowManager.Stub
implementsWatchdog.Monitor{
publicintaddWindow(Sessionsession,IWindowclient,
WindowManager.LayoutParamsattrs,intviewVisibility,
RectoutContentInsets,InputChanneloutInputChannel){
synchronized(mWindowMap){
WindowTokentoken=mTokenMap.get(attrs.token);
if(token==null){
if(attrs.type==TYPE_WALLPAPER){
returnWindowManagerImpl.ADD_BAD_APP_TOKEN;
win=newWindowState(session,client,token,
attachedWindow,attrs,viewVisibility);
if(attrs.type==TYPE_INPUT_METHOD){
}elseif(attrs.type==TYPE_INPUT_METHOD_DIALOG){
}else{
addWindowToListInOrderLocked(win,true);
adjustWallpaperWindowsLocked();
}elseif((attrs.flags&
FLAG_SHOW_WALLPAPER)!
=0){
assignLayersLocked();
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
如果当前增加到WindowManagerService服务来的是一个壁纸窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_WALLPAPER,那么就要求与该壁纸窗口所对应的类型为WindowToken的窗口令牌已经存在,否则的话,WindowManagerService类的成员函数addWindow就会直接返回一个错误码WindowManagerImpl.ADD_BAD_APP_TOKEN给调用者。
这个类型为WindowToken的窗口令牌是WallpaperManagerService服务请求WindowManagerService服务创建的,即调用WindowManagerService类的成员函数addWindowToken来创建的,具体可以参考前面一文。
如果当前增加到WindowManagerService服务来的既不是一个输入法窗口,也不是一个输入法对话框,那么WindowManagerService类的成员函数addWindow就会调用另外一个成员函数addWindowToListInOrderLocked来将前面为它所创建的一个WindowState对象win增加到窗口堆栈的合适位置上去。
如果前面增加到窗口堆栈中的窗口是一个壁纸窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_WALLPAPER,或者是一个需要显示壁纸的窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量flags的值的FLAG_SHOW_WALLPAPER位等于1,那么就说明需要调整壁纸窗口在窗口堆栈中的位置,使得它位于需要显示壁纸的窗口的下面,这是通过调用WindowManagerService类的成员函数adjustWallpaperWindowsLocked来实现的。
最后,由于增加了一个窗口到窗口堆栈中,以及窗口堆栈的窗口位置发生了变化,因此,就需要重新各个窗口的Z轴位置,这是通过调用WindowManagerService类的成员函数assignLayersLocked来实现的。
在这个情景中,主要涉及到了WindowManagerService类的三个成员函数addWindowToListInOrderLocked、adjustWallpaperWindowsLocked和assignLayersLocked,其中,成员函数addWindowToListInOrderLocked的实现可以参考前面前面一文,成员函数assignLayersLocked的实现在接下来的一篇文章中再分析,本文主要是关注成员函数adjustWallpaperWindowsLocked的实现。
第二个需要调整壁纸窗口在窗口堆栈中的位置的情景是一个应用程序进程请求WindowManagerService服务重新布局一个窗口的时候。
从前面一文可以知道,应用程序进程请求WindowManagerService服务重新布局一个窗口最终是通过调用WindowManagerService类的成员函数relayoutWindow来实现的。
publicintrelayoutWindow(Sessionsession,IWindowclient,
WindowManager.LayoutParamsattrs,intrequestedWidth,
intrequestedHeight,intviewVisibility,booleaninsetsPending,
RectoutFrame,RectoutContentInsets,RectoutVisibleInsets,
ConfigurationoutConfig,SurfaceoutSurface){
booleandisplayed=false;
WindowStatewin=windowForClientLocked(session,client,false);
intattrChanges=0;
if(attrs!
=null){
attrChanges=win.mAttrs.copyFrom(attrs);
booleanwallpaperMayMove=win.mViewVisibility!
=viewVisibility
&
&
(win.mAttrs.flags&
FLAG_SHOW_WALLPAPER)!
=0;
if(viewVisibility==View.VISIBLE&
(win.mAppToken==null||!
win.mAppToken.clientHidden)){
displayed=!
win.isVisibleLw();
if((attrChanges&
WindowManager.LayoutParams.FORMAT_CHANGED)!
//Tochangetheformat,weneedtore-buildthesurface.
win.destroySurfaceLocked();
displayed=true;
booleanassignLayers=false;
if(wallpaperMayMove){
if((adjustWallpaperWindowsLocked()&
ADJUST_WALLPAPER_LAYERS_CHANGED)!
assignLayers=true;
if(assignLayers){
performLayoutAndPlaceSurfacesLocked();
if(displayed&
win.mIsWallpaper){
updateWallpaperOffsetLocked(win,mDisplay.getWidth(),
mDisplay.getHeight(),false);
return(inTouchMode?
WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE:
0)
|(displayed?
WindowManagerImpl.RELAYOUT_FIRST_TIME:
0);
这个函数定义在文件frameworks/base/services/Java/com/android/server/WindowManagerService.java中。
应用程序进程在请求WindowManagerService服务重新布局一个窗口的时候,这个窗口的一些布局参数可能会发生变化,而这些变化可能会引发系统的壁纸窗口在窗口堆栈中的位置发生变化。
如果系统的壁纸窗口在窗口堆栈中的位置发生了变化,那么就需要调整它们在窗口堆栈中的位置。
WindowManagerService类的成员函数relayoutWindow首先调用根据参数session和client来调用另外一个成员函数windowForClientLocked,以便可以获得用来描述要重新布局的窗口的一个WindowState对象win。
WindowState对象win的成员变量mViewVisibility描述的是窗口上一次布局时的可见性,而参数viewVisibility描述的是窗口当前的可见性,当它们的值不相等时,就意味着窗口的可见性发生了变化。
在窗口的可见性发生了变化的情况下,如果正在请求重新布局的是一个需要显示壁纸的窗口,即WindowState对象win的成员变量mAttrs所指向的是一个WindowManager.LayoutParams对象的成员变量flags的FLAG_SHOW_WALLPAPER位等于1,那么就说明可能需要调整壁纸窗口在窗口堆栈中的位置,以便它可以位于WindowState对象win所描述的窗口的下面,这时候变量wallpaperMayMove的值就会等于true。
WindowManagerService类的成员函数relayoutWindow执行了一系列的其它操作之后,接下来就会判断变量wallpaperMayMove的值是否等于true。
如果等于true的话,那么就会调用另外一个成员函数adjustWallpaperWindowsLocked来调整壁纸窗口在窗口堆栈中的位置,以便它可以位于需要显示壁纸的窗口的下面。
WindowManagerService类的成员函数adjustWallpaperWindowsLocked的返回值是一个整数,当它的ADJUST_WALLPAPER_LAYERS_CHANGED位等于1的时候,就说明壁纸窗口在窗口堆栈的位置发生了变化,于是就会将变量assignLayers的值设置为true,以便接下来可以调用WindowManagerService类的成员函数assignLayersLocked来重新计算系统中各个窗品的Z轴位置。
变量displayed用来描述WindowState对象win所描述的窗口在当前布局中是由不可见变为可见的。
在满足以下的条件之下,WindowState对象win所描述的窗口是由不可见变为可见的:
1.参数viewVisibility的值等于View.VISIBLE,即应用程序进程请求显示WindowState对象win所描述的窗口。
2.WindowState对象win描述的是一个Activity窗口,即它的成员变量mAppToken不等于null,并且它所指向的AppWindowToken对象的成员变量clientHidden的值等于false,即WindowState对象win的窗口所对应的Activity组件当前是可见的。
注意,如果WindowState对象win描述的不是一个Activity窗口,即它的成员变量mAppToken等于null,那么就可以忽略条件2。
3.WindowState对象win所描述的窗口上一次是不可见的,即调用WindowState对象win的成员函数isVisibleLw的返回值等于false。
此外,在满足条件1和条件2的情况下,如果WindowState对象win所描述的窗口的像素格式发生了变化,那么就需要将该窗口的绘图表面销毁掉,然后再重新创建一个,这时候也会认为该窗口由不可见变为了可见。
参数attrs所指向的一个WindowManager.LayoutParams对象是用来保存WindowState对象win所描述的窗口在当前布局中所使用的布局参数的,而WindowState对象win的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象是用来保存WindowState对象win所描述的窗口在上一次布局所使用的布局参数的。
在将参数attrs所指向的一个WindowManager.LayoutParams对象的内容拷贝到WindowState对象win的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的过程中,如果某些布局参数发生了变化,那么就会记录在变量attrChanges中。
当变量attrChanges的WindowManager.LayoutParams.FORMAT_CHANGED位等于1时,就说明WindowState对象win所描述的窗口的像素格式发生了变化,因此,WindowManagerService类的成员函数relayoutWindow就会调用WindowState对象win的成员函数destroySurfaceLocked来销毁该窗口的绘图表面,并且将变量displayed的值设置为true。
WindowManagerService类的成员函数relayoutWindow调用另外一个成员函数performLayoutAndPlaceSurfacesLocked来对WindowState对象win所描述的窗口进行了布局之后,如果发现变量displayed的值等于true,并且WindowState对象win描述的是一个壁纸窗口,即它的成员变量mIsWallpaper的值等于true,那么还需要调用另外一个成员函数updateWallpaperOffsetLocked来重新计算该壁纸窗口在X轴和Y轴上的偏移位置,以便可以将它的某一部分区域指定在需要显示壁纸的窗口的背景。
在这个情景中,主要涉及到了WindowManagerService类的四个成员函数adjustWallpaperWindowsLocked、updateWallpaperOffsetLocked、performLayoutAndPlaceSurfacesLocked和assignLayersLocked,其中,成员函数performLayoutAndPlaceSurfacesLocked的实现框架可以参考前面一文,成员函数assignLayersLocked的实现如上所述在接下来的一篇文章中再分析,本文主要是关注成员函数adjustWallpaperWindowsLocked和updateWallpaperOffsetLocked的实现。
从上面的分析就可以知道,在布局一个窗口的过程中,可能需要调用WindowManagerService类的成员函数updateWallpaperOffsetLocked和adjustWallpaperWindowsLocked来调整壁纸窗口在X轴和Y轴上的偏移位置和在窗口堆栈中的位置。
接下来我们就分别分析壁纸窗口在X轴和Y轴上的偏移位置和在窗口堆栈中的位置的调整过程。
二.调整壁纸窗口在X轴和Y轴上的偏移位置
壁纸窗口的大小是可以大于屏幕大小的。
在这种情况下,需要显示壁纸的Activity窗口就需要指定壁纸在X轴和Y轴上的偏移位置,以便可以将壁纸的某一部分作为窗口的背景。
假设壁纸窗口的大小为(WallpaperWidth,WallpaperHeight),屏幕的大小为(DisplayWidth,DisplayHeight),并且壁纸在X轴和Y轴上的偏移位置为WallpaperX和WallpaperY,其中,WallpaperWidth>
Di