Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx

上传人:b****5 文档编号:11622826 上传时间:2023-03-28 格式:DOCX 页数:48 大小:226.37KB
下载 相关 举报
Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx_第1页
第1页 / 共48页
Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx_第2页
第2页 / 共48页
Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx_第3页
第3页 / 共48页
Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx_第4页
第4页 / 共48页
Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx_第5页
第5页 / 共48页
点击查看更多>>
下载资源
资源描述

Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx

《Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx》由会员分享,可在线阅读,更多相关《Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx(48页珍藏版)》请在冰豆网上搜索。

Android窗口管理服务WindowManagerService显示Activity组件的启动窗口Starting Window的过程分析.docx

Android窗口管理服务WindowManagerService显示Activity组件的启动窗口StartingWindow的过程分析

Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(StartingWindow)的过程分析

在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口。

这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerService服务统一管理的,即由WindowManagerService服务负责启动和结束。

在本文中,我们就详细分析WindowManagerService服务启动和结束Activity组件的启动窗口的过程。

Activity组件的启动窗口是由ActivityManagerService服务来决定是否要显示的。

如果需要显示,那么ActivityManagerService服务就会通知WindowManagerService服务来为正在启动的Activity组件显示一个启动窗口,而WindowManagerService服务又是通过窗口管理策略类PhoneWindowManager来创建这个启动窗口的。

这个过程如图1所示。

窗口管理策略类PhoneWindowManager创建完成Activity组件的启动窗口之后,就会请求WindowManagerService服务将该启动窗口显示出来。

当Activity组件启动完成,并且它的窗口也显示出来的时候,WindowManagerService服务就会结束显示它的启动窗口。

注意,Activity组件的启动窗口是由ActivityManagerService服务来控制是否显示的,也就是说,Android应用程序是无法决定是否要要Activity组件显示启动窗口的。

接下来,我们就分别分析Activity组件的启动窗口的显示和结束过程。

一.Activity组件的启动窗口的显示过程

从前面一文可以知道,Activity组件在启动的过程中,会调用ActivityStack类的成员函数startActivityLocked。

注意,在调用ActivityStack类的成员函数startActivityLocked的时候,Actvitiy组件还处于启动的过程,即它的窗口尚未显示出来,不过这时候ActivityManagerService服务会检查是否需要为正在启动的Activity组件显示一个启动窗口。

如果需要的话,那么ActivityManagerService服务就会请求WindowManagerService服务为正在启动的Activity组件设置一个启动窗口。

这个过程如图2所示。

这个过程可以分为6个步骤,接下来我们就详细分析每一个步骤。

Step1.ActivityStack.startActivityLocked

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassActivityStack{

......

//Settofalsetodisablethepreviewthatisshownwhileanewactivity

//isbeingstarted.

staticfinalbooleanSHOW_APP_STARTING_PREVIEW=true;

......

privatefinalvoidstartActivityLocked(ActivityRecordr,booleannewTask,

booleandoResume){

finalintNH=mHistory.size();

......

intaddPos=-1;

......

//Placeanewactivityattopofstack,soitisnexttointeract

//withtheuser.

if(addPos<0){

addPos=NH;

}

......

//Slottheactivityintothehistorystackandproceed

mHistory.add(addPos,r);

......

if(NH>0){

//Wewanttoshowthestartingpreviewwindowifweare

//switchingtoanewtask,orthenextactivity'sprocessis

//notcurrentlyrunning.

booleanshowStartingIcon=newTask;

ProcessRecordproc=r.app;

if(proc==null){

proc=mService.mProcessNames.get(r.processName,r.info.applicationInfo.uid);

}

if(proc==null||proc.thread==null){

showStartingIcon=true;

}

......

mService.mWindowManager.addAppToken(

addPos,r,r.task.taskId,r.info.screenOrientation,r.fullscreen);

booleandoShow=true;

if(newTask){

//Eventhoughthisactivityisstartingfresh,westillneed

//toresetittomakesureweapplyaffinitiestomoveany

//existingactivitiesfromothertasksintoit.

//Ifthecallerhasrequestedthatthetargettaskbe

//reset,thendoso.

if((r.intent.getFlags()

&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)!

=0){

resetTaskIfNeededLocked(r,r);

doShow=topRunningNonDelayedActivityLocked(null)==r;

}

}

if(SHOW_APP_STARTING_PREVIEW&&doShow){

//Figureoutifwearetransitioningfromanotheractivitythatis

//"hasthesamestartingicon"asthenextone.Thisallowsthe

//windowmanagertokeepthepreviouswindowithadpreviously

//created,ifitstillhadone.

ActivityRecordprev=mResumedActivity;

if(prev!

=null){

//Wedon'twanttoreusethepreviousstartingpreviewif:

//

(1)Thecurrentactivityisinadifferenttask.

if(prev.task!

=r.task)prev=null;

//

(2)Thecurrentactivityisalreadydisplayed.

elseif(prev.nowVisible)prev=null;

}

mService.mWindowManager.setAppStartingWindow(

r,r.packageName,r.theme,r.nonLocalizedLabel,

r.labelRes,r.icon,prev,showStartingIcon);

}

}else{

//Ifthisisthefirstactivity,don'tdoanyfancyanimations,

//becausethereisnothingforittoanimateontopof.

mService.mWindowManager.addAppToken(addPos,r,r.task.taskId,

r.info.screenOrientation,r.fullscreen);

}

......

if(doResume){

resumeTopActivityLocked(null);

}

}

......

}

这个函数定义在文件frameworks/base/services/Java/com/android/server/am/ActivityStack.java中。

参数r描述的就是正在启动的Activity组件,而参数newTask和doResume描述的是是否要将该Activity组件放在一个新的任务中启动,以及是否要马上将该Activity组件启动起来。

ActivityStack类的成员变量mHistory指向的是一个ArrayList,它描述的便是系统的Activity组件堆栈。

ActivityStack类的成员函数startActivityLocked首先找到正在启动的Activity组件r在系统的Activity组件堆栈中的位置addPos,然后再将正在启动的Activity组件r保存在这个位置上。

变量NH记录的是将正在启动的Activity组件r插入到系统的Activity组件堆栈中之前系统中已经启动了的Activity组件的个数。

如果变量NH的值大于0,那么就说明系统需要执行一个Activity组件切换操作,即需要在系统当前激活的Activity组件和正在启动的Activity组件r之间执行一个切换操作,使得正在启动的Activity组件r成为系统接下来要激活的Activity组件。

在切换的过程,需要显示切换动画,即给系统当前激活的Activity组件显示一个退出动画,而给正在启动的Activity组件r显示一个启动动画,以及需要为正在启动的Activity组件r显示一个启动窗口。

另一方面,如果变量NH的值等于0,那么系统就不需要执行Activity组件切换操作,或者为为正在启动的Activity组件r显示一个启动窗口,这时候只需要为正在启动的Activity组件r创建一个窗口令牌即可。

ActivityStack类的成员变量mService指向的是一个ActivityManagerService对象,这个ActivityManagerService对象就是系统的Activity组件管理服务,它的成员变量mWindowManager指向的是一个WindowManagerService对象,这个WindowManagerService对象也就是系统的Window管理服务。

通过调用WindowManagerService类的成员函数addAppToken就可以为正在启动的Activity组件r创建一个窗口令牌,这个过程可以参考前面一文。

在变量NH的值大于0的情况下,ActivityStack类的成员函数startActivityLocked首先检查用来运行Activity组件r的进程是否已经启动起来了。

如果已经启动起来,那么用来描述这个进程的ProcessRecord对象proc的值就不等于null,并且这个ProcessRecord对象proc的成员变量thread的值也不等于null。

如果用来运行Activity组件r的进程还没有启动起来,或者Activity组件r需要运行在一个新的任务中,那么变量showStartingIcon的值就会等于true,用来描述在系统当前处于激活状态的Activity组件没有启动窗口的情况下,要为Activity组件r创建一个新的启动窗口,否则的话,就会将系统当前处于激活状态的Activity组件的启动窗口复用为Activity组件r的启动窗口。

系统当前处于激活状态的Activity组件是通过ActivityStack类的成员变量mResumedActivity来描述的,它的启动窗口可以复用为Activity组件r的启动窗口还需要满足两个额外的条件:

1.Activity组件mResumedActivity与Activity组件r运行在同一个任务中,即它们的成员变量task指向的是同一个TaskRecord对象;

2.Activity组件mResumedActivity当前是不可见的,即它的成员变量nowVisible的值等于false。

这两个条件意味着Activity组件mResumedActivity与Activity组件r运行在同一个任务中,并且Activity组件mResumedActivity的窗口还没有显示出来就需要切换到Activity组件r去。

ActivityStack类的静态成员变量SHOW_APP_STARTING_PREVIEW是用描述系统是否可以为正在启动的Activity组件显示启动窗口,只有在它的值等于true,以及正在启动的Activity组件的窗口接下来是要显示出来的情况下,即变量doShow的值等于true,ActivityManagerService服务才会请求WindowManagerService服务为正在启动的Activity组件设置启动窗口。

一般来说,一个正在启动的Activity组件的窗口接下来是需要显示的,但是正在启动的Activity组件可能会设置一个标志位,用来通知ActivityManagerService服务在它启动的时候,对它运行在的任务进行重置。

一个任务被重置之后,可能会导致其它的Activity组件转移到这个任务中来,并且位于位于这个任务的顶端。

在这种情况下,系统接下来要显示的窗口就不是正在启动的Activity组件的窗口的了,而是位于正在启动的Activity组件所运行在的任务的顶端的那个Activity组件的窗口。

正在启动的Activity组件所运行在的任务同时也是一个前台任务,即它顶端的Activity组件就是系统Activity组件堆栈顶端的Activity组件。

调用参数r所指向一个ActivityRecord对象的成员变量intent所描述的一个Intent对象的成员函数getFlags就可以获得正在启动的Activity组件的标志值,当这个标志值的FLAG_ACTIVITY_RESET_TASK_IF_NEEDED位等于1的时候,就说明正在启动的Activity组件通知ActivityManagerService服务对它运行在的任务进行重置。

重置一个任务是通过调用ActivityStack类的成员函数resetTaskIfNeededLocked来实现的。

重置了正在启动的Activity组件所运行在的任务之后,再调用ActivityStack类的成员函数topRunningNonDelayedActivityLocked来检查位于系统Activity组件堆栈顶端的Activity组件是否就是正在启动的Activity组件,就可以知道正在启动的Activity组件的窗口接下来是否是需要显示的。

如果需要显示的话,那么变量doShow的值就等于true。

ActivityManagerService服务请求WindowManagerService服务为正在启动的Activity组件设置启动窗口是通过调用WindowManagerService类的成员函数setAppStartingWindow来实现的。

注意,ActivityManagerService服务在请求WindowManagerService服务为正在启动的Activity组件设置启动窗口之前,同样会调用WindowManagerService类的成员函数addAppToken来创建窗口令牌。

ActivityManagerService服务请求WindowManagerService服务为正在启动的Activity组件设置启动窗口之后,如果参数doResume的值等于true,那么就会调用ActivityStack类的成员函数resumeTopActivityLocked继续执行启动参数r所描述的一个Activity组件的操作,这个过程可以参考前面一文。

接下来,我们就继续分析WindowManagerService类的成员函数setAppStartingWindow的实现,以便可以了解WindowManagerService服务是如何为正在启动的Activity组件设置启动窗口的。

Step2.WindowManagerService.setAppStartingWindow

WindowManagerService类的成员函数setAppStartingWindow定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的实现比较长,我们分段来阅读:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicclassWindowManagerServiceextendsIWindowManager.Stub

implementsWatchdog.Monitor{

......

publicvoidsetAppStartingWindow(IBindertoken,Stringpkg,

inttheme,CharSequencenonLocalizedLabel,intlabelRes,inticon,

IBindertransferFrom,booleancreateIfNeeded){

......

synchronized(mWindowMap){

......

AppWindowTokenwtoken=findAppWindowToken(token);

......

//Ifthedisplayisfrozen,wewon'tdoanythinguntilthe

//actualwindowisdisplayedsothereisnoreasontoputin

//thestartingwindow.

if(mDisplayFrozen||!

mPolicy.isScreenOn()){

return;

}

if(wtoken.startingData!

=null){

return;

}

参数token描述的是要设置启动窗口的Activity组件,而参数transferFrom描述的是要将启动窗口转移给Activity组件token的Activity组件。

从Step1可以知道,这两个Activity组件是运行在同一个任务中的,并且参数token描述的Activity组件Activity组件是正在启动的Activity组件,而参数transferFrom描述的Activity组件是系统当前激活的Activity组件。

这段代码首先调用WindowManagerService类的成员函数findAppWindowToken来获得与参数token对应的一个类型为AppWindowToken的窗口令牌wtoken。

如果这个AppWindowToken对象的成员变量startingData的值不等于null,那么就说明参数token所描述的Activity组件已经设置过启动窗口了,因此,WindowManagerService类的成员函数setAppStartingWindow就不用往下处理了。

这段代码还会检查系统屏幕当前是否处于冻结状态,即WindowManagerService类的成员变量mDisplayFrozen的值是否等于true,或者系统屏幕当前是否处于黑屏状态,即indowManagerService类的成员变量mPolicy所指向的一个PhoneWindowManager对象的成员函数isScreenOn的返回值是否等于false。

如果是处于上述两种状态的话,那么WindowManagerService类的成员函数setAppStartingWindow就不用往下处理的。

因为在这两种状态下,为token所描述的Activity组件设置的启动窗口是无法显示的。

我们接着往下阅读代码:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

if(transferFrom!

=null){

AppWindowTokenttoken=findAppWindowToken(transferFrom);

if(ttoken!

=null){

WindowStatestartingWindow=ttoken.startingWindow;

if(startingWindow!

=null){

if(mStartingIconInTransition){

//Inthiscase,thestartingiconhasalready

//beendisplayed,sostartlettingwindowsget

//shownimmediatelywithoutanymoretransitions.

mSkipAppTransitionAnimation=true;

}

......

finallongorigId=Binder.clearCallingIdentity();

//Transferthestartingwindowovertothenew

//token.

wtoken.startingData=ttoken.startingData;

wtoken.startingView=ttoken.startingView;

wtoken.startingWindow=startingWindow;

ttoken.startingData=null;

ttoken.starti

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1