在Android系统中,同一时刻只有一个Activity组件是处于激活状态的,因此,当ActivityManagerService服务激活了一个新的Activity组件时,它就需要通知WindowManagerService服务将该Activity组件的窗口显示出来,这会涉及到将焦点和屏幕等资源从前一个激活的Activity组件切换到后一个激活的Activity组件的过程,本文就详细分析这个过程。
Activity窗口的切换操作是在新激活的Activity组件的启动过程进行的。具体来说,就是在前一个激活的Activity组件进入到Paused状态并且新激活的Activity组件进之到Resumed 状态之后,将前一个激活的Activity组件的窗口设置为不可见,以及将新激活的Activity组件的窗口设置为可见。整个切换过程是需要在ActivityManagerService服务和WindowManagerService服务的协作之下进行的,如图1所示。
图1 Activity窗口的切换操作示意图
WindowManagerService服务在执行Activity窗口的切换操作的时候,会给参与切换操作的Activity组件的设置一个动画,以便可以向用户展现一个Activity组件切换效果,从而提高用户体验。事实上,一个Activity窗口在由不可见状态切换到可见状态的过程中,除了会被设置一个Activity组件切换动画之外,还有被设置一个窗口进入动画,此外,如果该Activity窗口是附加在另外一个窗口之上的,并且这个被附加的窗口正在显示一个动画,那么这个动画也同时会被设置给到该Activity窗口的显示过程中去。本文主要是关注Activity窗口的切换操作,在接下来的一篇文章中分析窗口的动画框架时,我们再详细分析上述三种动画是如何作用在窗口的显示过程中的。
从前面Android应用程序启动过程源代码分析一文可以知道,ActivityManagerService服务在启动一个Activity组件的过程中,会调用到ActivityStack类的成员函数startActivityLocked。ActivityStack类的成员函数startActivityLocked首先会给正在启动的Activity组件准备一个切换操作,接着再调用其它的成员函数来通知前一个激活的Activity组件进入到Paused状态。等到前一个激活的Activity组件进入到Paused状态之后,ActivityManagerService服务就会检查用来运行正在启动的Activity组件的进程启动起来了没有。如果这个进程还没有启动,那么ActivityManagerService服务就会将该进程启动起来,然后再调用ActivityStack类的成员函数realStartActivityLocked来将正在启动的Activity组件加载起来,并且将它的状态设置为Resumed,最后通知WindowManagerService服务执行前面所准备的切换操作。
接下来,我们就从ActivityStack类的成员函数startActivityLocked开始分析Activity窗口的切换过程,如图2所示。
图2 Activity窗口的切换过程
这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。
Step 1. ActivityStack.startActivityLocked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public
class
ActivityStack {
......
private
final
void
startActivityLocked(ActivityRecord r,
boolean
newTask,
boolean
doResume) {
final
int
NH = mHistory.size();
int
addPos = -
1
;
if
(!newTask) {
// If starting in an existing task, find where that is...
......
}
// Place a new activity at top of stack, so it is next to interact
// with the user.
if
(addPos <
0
) {
addPos = NH;
}
......
// Slot the activity into the history stack and proceed
mHistory.add(addPos, r);
......
if
(NH >
0
) {
......
if
((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) !=
0
) {
mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
......
}
else
if
((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) !=
0
) {
mService.mWindowManager.prepareAppTransition(
WindowManagerPolicy.TRANSIT_TASK_OPEN);
......
}
else
{
mService.mWindowManager.prepareAppTransition(newTask
? WindowManagerPolicy.TRANSIT_TASK_OPEN
: WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
......
}
......
}
......
if
(doResume) {
resumeTopActivityLocked(
null
);
}
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。
参数r描述的是正在启动的Activity组件,ActivityStack类的成员函数startActivityLocked首先找到它在Activity组件堆栈中的位置addPos,即找到它在ActivityStack类的成员变量mHistory所描述的一个ArrayList中的位置,然后再将正在启动的Activity组件保存在该位置中。
变量NH描述的是将参数r描述的是正在启动的Activity组件保存在Activity组件堆栈前系统已经启动了的Activity组件的个数。只有在这个变量的值大于0的情况下,系统才需要执行一个Activity组件切换操作。也就是说,如果参数r描述的是正在启动的Activity组件是系统中第一个启动的Activity组件,那么就不需要执行一个Activity组件切换操作了。
注意,即使参数r描述的是正在启动的Activity组件不是系统中第一个启动的Activity组件,那么系统也可能不需要执行一个Activity组件切换操作,因为用来启动参数r所描述的一个Activity组件的一个Intent对象的成员函数getFlags返回的一个标志值的Intent.FLAG_ACTIVITY_NO_ANIMATION位可能会不等于0,这意味着正在启动的Activity组件不需要显示切换动画。在这种情况下,ActivityManagerSerivce服务就会通知WindowManagerService服务不需要准备一个Activity组件切换操作,这是通过以WindowManagerPolicy.TRANSIT_NONE为参数来调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数prepareAppTransition来实现的。
另一方面,如果参数r描述的是正在启动的Activity组件不是系统中第一个启动的Activity组件,并且系统需要执行一个Activity组件切换操作,即需要WindowManagerService服务在显示正在启动的Activity组件的窗口时应用一个切换动画,那么这个动画的类型也是有讲究的。具体来说,如果参数r描述的是Activity组件是需要在在一个新的任务中启动的,即参数newTask的值等于true,那么切换动画的类型就指定为WindowManagerPolicy.TRANSIT_TASK_OPEN,否则的话,切换动画的类型就指定为WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN。
此外,如果用来启动参数r所描述的一个Activity组件的一个Intent对象的成员函数getFlags返回的一个标志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位不等于0,那么也会将切换动画的类型设置为WindowManagerPolicy.TRANSIT_TASK_OPEN。Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET这个标志位等于1意味着当参数r所描述的一个Activity组件所在的任务下次作为前台任务来运行时,参数r所描述的Activity组件以及它上面的并且属于同一个任务的其它Activity组件都会被结束掉,以使得位于参数r所描述的Activity组件的前面一个Activity组件可以显示出来。例如,如果我们正在使用一个Email Activity组件来查看Email,这时候又需要启动另外一个Pictrue Activity来查看该Email附件的一张图片。这时候Email Activity和Pictrue Activity就是在同一个任务中的。突然间,我们因为其它原历,需要按下Home键回到Launcher中去完成其它任务。完成其它任务之后,再点击Launcher上面的Email Activity图标来想重新浏览之前正在查看的Email,但是又不想看到的是该Email附件的那张图片。在这种情况下,之前在启动Pictrue Activity来查看Email的附件图片时,就可以将用来启动Pictrue Activity的一个Intent对象的标志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位设置为1。
无论如何,最终指定的切换动画的类型都是通过调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数prepareAppTransition来通知WindowManagerService服务的。
ActivityStack类的成员函数startActivityLocked通知WindowManagerService服务准备好一个Activity组件切换操作之后,如果参数doResume的值等于true,那么它就会继续调用另外一个成员函数resumeTopActivityLocked来继续执行启动参数r所描述的一个Activity组件的操作。
接下来,我们就首先分析WindowManagerService类的成员函数prepareAppTransition的实现,以便可以了解WindowManagerService服务是如何准备一个Activity组件切换操作的,然后再回过头来分析ActivityStack类的成员函数resumeTopActivityLocked是如何继续执行启动参数r所描述的一个Activity组件的操作的。
Step 2. WindowManagerService.prepareAppTransition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public
class
WindowManagerService
extends
IWindowManager.Stub
implements
Watchdog.Monitor {
......
// State management of app transitions. When we are preparing for a
// transition, mNextAppTransition will be the kind of transition to
// perform or TRANSIT_NONE if we are not waiting. If we are waiting,
// mOpeningApps and mClosingApps are the lists of tokens that will be
// made visible or hidden at the next transition.
int
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
......
boolean
mAppTransitionReady =
false
;
......
boolean
mAppTransitionTimeout =
false
;
boolean
mStartingIconInTransition =
false
;
boolean
mSkipAppTransitionAnimation =
false
;
public
void
prepareAppTransition(
int
transit) {
if
(!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()"
)) {
throw
new
SecurityException(
"Requires MANAGE_APP_TOKENS permission"
);
}
synchronized
(mWindowMap) {
......
if
(!mDisplayFrozen && mPolicy.isScreenOn()) {
if
(mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
|| mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
mNextAppTransition = transit;
}
else
if
(transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
&& mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
// Opening a new task always supersedes a close for the anim.
mNextAppTransition = transit;
}
else
if
(transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
&& mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
// Opening a new activity always supersedes a close for the anim.
mNextAppTransition = transit;
}
mAppTransitionReady =
false
;
mAppTransitionTimeout =
false
;
mStartingIconInTransition =
false
;
mSkipAppTransitionAnimation =
false
;
mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT),
5000
);
}
}
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
调用WindowManagerService类的成员函数prepareAppTransition来通知WindowManagerService服务准备一个Activity组件切换操作是需要具有android.Manifest.permission.MANAGE_APP_TOKENS,否则的话,WindowManagerService类的成员函数prepareAppTransition就会抛出一个类型为SecurityException的异常。
WindowManagerService类的成员变量mNextAppTransition描述的就是WindowManagerService服务接下来要执行一个Activity组件切换操作的类型,也就是要执行的一个Activity组件切换动画的类型。WindowManagerService类的成员函数prepareAppTransition按照以下规则来设置WindowManagerService服务接下来要执行的Activity组件切换动画的类型:
1. 当 WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET或者WindowManagerPolicy.TRANSIT_NONE的时候,就说明WindowManagerService服务接下来没有Activity组件切换动画等待执行的,这时候参数transit所描述的Activity组件切换动画就可以作为WindowManagerService服务接下来要执行的Activity组件切换动画。
2. 当WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_TASK_CLOSE,那么就说明WindowManagerService服务接下来要执行一个关闭Activity组件任务的切换动画等待执行的。在这种情况下,如果参数transit所描述的是一个打开Activity组件任务的切换动画,即它的值等于WindowManagerPolicy.TRANSIT_TASK_OPEN,那么就需要将WindowManagerService服务接下来要执行的Activity组件切换动画为打开Activity组件任务类型的。这是因为打开Activity组件任务的切换动画的优先级高于关闭Activity组件任务的切换动画。
3. 当WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE,那么就说明WindowManagerService服务接下来要执行一个关闭Activity组件的切换动画等待执行的。在这种情况下,如果参数transit所描述的是一个打开Activity组件的切换动画,即它的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN,那么就需要将WindowManagerService服务接下来要执行的Activity组件切换动画为打开Activity组件类型的。这是因为打开Activity组件的切换动画的优先级高于关闭Activity组件的切换动画。
设置好WindowManagerService服务接下来要执行的Activity组件切换动画的类型之后,WindowManagerService类的成员函数prepareAppTransition还会将其余四个成员变量mAppTransitionReady、mAppTransitionTimeout、mStartingIconInTransition和mSkipAppTransitionAnimation的值设置为false,其中:
1. mAppTransitionReady表示WindowManagerService服务可以开始执行一个Activity组件的切换动画了没有?
2. mAppTransitionTimeout表示WindowManagerService服务正在执行的Activity组件切换动画是否已经超时?
3. mStartingIconInTransition表示WindowManagerService服务开始显示正在启动的Activity组件的启动窗口了没有?
4. mSkipAppTransitionAnimation表示WindowManagerService服务是否需要不执行Activity组件的切换动画?
最后,WindowManagerService类的成员函数prepareAppTransition还会调用成员变量mH所描述的一个H对象的成员函数sendMessageDelayed来向WindowManagerService服务所运行在的线程发送一个类型为APP_TRANSITION_TIMEOUT的消息。这个消息将在5秒后被执行,是用来强制前面所设置的Activity组件切换动画要在5秒之内执行完成的,否则的话,WindowManagerService服务就会认为该切换动画执行超时了。
这一步执行完成之后,WindowManagerService服务接下来要执行的Activity组件切换操作或者说切换动画就准备完成了。注意,这时候只是准备好Activity组件切换动画,但是这个切换动画还不能执行,要等到前一个激活的Activity组件进入到Paused状态并且接下来正在启动的Activity组件进入到Resumed状态之后才能执行。
回到前面的Step 1中,即ActivityStack类的成员函数startActivityLocked,接下来它就会调用另外一个成员函数resumeTopActivityLocked来继续启动指定的Activity组件。从前面Android应用程序启动过程源代码分析一文可以知道,ActivityStack类的成员函数resumeTopActivityLocked以及接下来要调用的其它成员函数就是执行以下两个操作:
1. 通知当前处于激活状态的Activity组件所运行在的应用程序进程,它所运行的一个Activity组件要由Resumed状态进入到Paused状态了。
2. 检查用来运行当前正在启动的Activity组件的应用程序进程是否已经启动起来了。如果已经启动起来,那么就会直接通知该应用程序进程将正在启动的Activity组件加载起来,否则的话,就会先将该应用程序进程启动起来,然后再通知它将正在启动的Activity组件加载起来。
在第2步中,通知相应的应用程序进程将正在启动的Activity组件加载起来是通过调用ActivityStack类的成员函数realStartActivityLocked来实现的,接下来我们就继续分析这个成员函数的实现,以便可以继续了解Activity组件的切换操作的执行过程。
Step 3. ActivityStack.realStartActivityLocked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public
class
ActivityStack {
......
final
boolean
realStartActivityLocked(ActivityRecord r,
ProcessRecord app,
boolean
andResume,
boolean
checkConfig)
throws
RemoteException {
......
mService.mWindowManager.setAppVisibility(r,
true
);
......
try
{
......
app.thread.scheduleLaunchActivity(
new
Intent(r.intent), r,
System.identityHashCode(r),
r.info, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward());
......
}
catch
(RemoteException e) {
......
}
......
if
(andResume) {
// As part of the process of launching, ActivityThread also performs
// a resume.
r.state = ActivityState.RESUMED;
......
mResumedActivity = r;
......
completeResumeLocked(r);
......
}
......
return
true
;
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。
ActivityStack类的成员函数realStartActivityLocked主要执行以下三个操作:
1. 通知WindowManagerService服务将参数r所描述的Activity组件的可见性设置为true,这是通过调用ActivityStack类的成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数setAppVisibility来实现的。
2. 通知参数r所描述的Activity组件所运行在的应用程序进程将它加载起来,这是通过调用参数app所指向的一个ProcessRecord对象的成员变量thread所描述的一个类型为ApplictionThread的Binder代理对象的成员函数scheduleLaunchActivity来实现的。
3. 当参数andResume的值等于true的时候,就表示在执行第2步时,参数r所描述的Activity组件所运行在的应用程序进程已经将它的状态设置为Resumed了,即已经调用过它的成员函数onResume了。在这种情况,ActivityManagerService服务也需要将该Activity组件的状态设置为Resumed了,即将r所指向的一个ActivityRecord对象的成员变量state的值设置为ActivityState.RESUMED,并且将ActivityStack类的成员变量mResumedActivity的值设置为r,以便表示当前激活的Activity组件为参数r所描述的Activity组件。最后,ActivityManagerService服务还需要调用ActivityStack类的成员函数completeResumeLocked来通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作。
接下来,我们首先分析WindowManagerService类的成员函数setAppVisibility的实现,以便可以了解WindowManagerService服务是如何设置一个Activity组件的可见性的,接着再分析ActivityStack类的成员函数completeResumeLocked的实现,以便可以了解ActivityManagerService服务是如何通知WindowManagerService服务执行前面所准备好的一个Activity组件切换操作的。
Step 4. WindowManagerService.setAppVisibility
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
public
class
WindowManagerService
extends
IWindowManager.Stub
implements
Watchdog.Monitor {
......
public
void
setAppVisibility(IBinder token,
boolean
visible) {
if
(!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()"
)) {
throw
new
SecurityException(
"Requires MANAGE_APP_TOKENS permission"
);
}
AppWindowToken wtoken;
synchronized
(mWindowMap) {
wtoken = findAppWindowToken(token);
......
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
if
(!mDisplayFrozen && mPolicy.isScreenOn()
&& mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
// Already in requested state, don't do anything more.
if
(wtoken.hiddenRequested != visible) {
return
;
}
wtoken.hiddenRequested = !visible;
......
wtoken.setDummyAnimation();
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
wtoken.waitingToShow = wtoken.waitingToHide =
false
;
wtoken.inPendingTransaction =
true
;
if
(visible) {
mOpeningApps.add(wtoken);
wtoken.startingDisplayed =
false
;
wtoken.startingMoved =
false
;
// If the token is currently hidden (should be the
// common case), then we need to set up to wait for
// its windows to be ready.
if
(wtoken.hidden) {
wtoken.allDrawn =
false
;
wtoken.waitingToShow =
true
;
if
(wtoken.clientHidden) {
// In the case where we are making an app visible
// but holding off for a transition, we still need
// to tell the client to make its windows visible so
// they get drawn. Otherwise, we will wait on
// performing the transition until all windows have
// been drawn, they never will be, and we are sad.
wtoken.clientHidden =
false
;
wtoken.sendAppVisibilityToClients();
}
}
}
else
{
mClosingApps.add(wtoken);
// If the token is currently visible (should be the
// common case), then set up to wait for it to be hidden.
if
(!wtoken.hidden) {
wtoken.waitingToHide =
true
;
}
}
return
;
}
......
setTokenVisibilityLocked(wtoken,
null
, visible, WindowManagerPolicy.TRANSIT_UNSET,
true
);
wtoken.updateReportedVisibilityLocked();
......
}
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
调用WindowManagerService类的成员函数setAppVisibility来设置Activity组件的可见性需要具有android.Manifest.permission.MANAGE_APP_TOKENS权限,否则的话,WindowManagerService类的成员函数setAppVisibility就会抛出一个类型为SecurityException的异常。
从前面Android窗口管理服务WindowManagerService对窗口的组织方式分析一文可以知道,每一个Activity组件在WindowManagerService服务内部都对应有一个AppWindowToken对象,用来描述该Activity组件的窗口在WindowManagerService服务中的状态。因此,WindowManagerService类的成员函数setAppVisibility就先通过调用成员函数findAppWindowToken来找到与参数token所描述的一个Activity组件所对应的一个AppWindowToken对象wtoken,以便接下来可以设置它的状态。
注意,WindowManagerService类的成员函数setAppVisibility在修改参数token所描述的一个Activity组件的可见性的时候,需要考虑WindowManagerService服务接下来是否需要执行一个Activity组件操作,即WindowManagerService类的成员变量mNextAppTransition的值是否等于WindowManagerPolicy.TRANSIT_UNSET。如果等于的话,那么参数token所描述的Activity组件就是正在等待执行切换操作的Activity组件,这时候修改的可见性就会复杂一些,否则的话,只要简单地执行以下两个操作即可:
1. 调用WindowManagerService类的成员函数setTokenVisibilityLocked来将参数token所描述的Activity组件的可见性设置为参数visible所描述的值;
2. 调用AppWindowToken对象wtoken的成员函数updateReportedVisibilityLocked来向ActivityManagerService服务报告参数token所描述的Activity组件的可见性。
我们假设WindowManagerService服务接下来需要执行一个Activity组件操作,即WindowManagerService类的成员变量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET,这时候还需要满足三个额外的条件,即:
1. 屏幕当前不是处于冻结状态,即WindowManagerService类的成员变量mDisplayFrozen的值等于false;
2. 屏幕当前是点亮的,即WindowManagerService类的成员变量mPolicy所指向的一个PhoneWindowManager对象的成员函数isScreenOn的返回值等于true;
3. 参数token所描述的一个Activity组件的可见性已经不等于所要设置的可见性,即前面所找到的AppWindowToken对象wtoken的成员变量hiddenRequested的值不等于参数visible的值。
那么WindowManagerService类的成员函数setAppVisibility接下来才会开始修改参数token所描述的一个Activity组件的可见性,即:
1. 将AppWindowToken对象wtoken的成员变量hiddenRequested的值设置为参数visible的相反值。也就是说,如果参数token所描述的Activity组件是可见的,那么就将AppWindowToken对象wtoken的成员变量hiddenRequested的值设置为false,否则的话,就设置为true。
2. 调用AppWindowToken对象wtoken的成员函数setDummyAnimation来给参数token所描述的Activity组件设置一个哑动画。注意,要等到执行参数token所描述的Activity组件的切换操作时,WindowManagerService服务才会给该Activity组件设置一个合适的切换动画。
3. 分别将AppWindowToken对象wtoken从WindowManagerService类的成员变量mOpeningApps和mClosingApps所描述的两个ArrayList中删除。注意,WindowManagerService类的成员变量mOpeningApps和mClosingApps保存的分别是系统当前正在打开和关闭的Activity组件,后面会根据参数visible的值来决定参数token所描述的Activity组件是正在打开的还是正在关闭的,以便可以将它放在WindowManagerService类的成员变量mOpeningApps或者mClosingApps中。
4. 将AppWindowToken对象wtoken的成员变量waitingToShow和waitingToHide的值都初始化为false,表示参数token所描述的Activity组件既不是正在等待显示的,也不是正在等待隐藏的,这两个成员变量的值同样是要根据参数visible的值来设置的。
5. 将AppWindowToken对象wtoken的成员变量inPendingTransaction的值设置为true,表示参数token所描述的Activity组件正在等待切换。
接下来的操作取决于参数visible的值是true还是false。
假设参数visible的值等于true,那么就表示要将参数token所描述的Activity组件设置可见,这时候WindowManagerService类的成员函数setAppVisibility会继续执行以下操作:
1. 将AppWindowToken对象wtoken添加WindowManagerService类的成员变量mOpeningApps所描述的一个ArrayList中,表示参数token所描述的Activity组件是正在打开的。
2. 将AppWindowToken对象wtoken的成员变量startingDisplayed和startingMoved的值都设置为false,表示参数token所描述的Activity组件的启动窗口还没有显示出来,以及也没有被转移给其它的Activity组件。
3. 如果AppWindowToken对象wtoken的成员变量hidden的值等于true,那么就意味着参数token所描述的Activity组件当前是不可见的。由于在这种情况下,参数token所描述的Activity组件正在等待打开,因此,该Activity组件的窗口一定是还没有绘制出来,并且正在等待绘制以及显示出来,这时候就需要将AppWindowToken对象wtoken的成员变量allDrawn和waitingToShow的值分别设置为false和true。
4. 如果AppWindowToken对象wtoken的成员变量hidden的值等于true,并且另外一个成员变量clientHidden的值也等于true,那么就说明在应用程序进程这一侧看来,参数token所描述的Activity组件是不可见的,这时候就需要让该应用程序进程认为参数token所描述的Activity组件是可见的,以便该应用程序进程可以将参数token所描述的Activity组件的窗口绘制出来,这样WindowManagerService服务接下来才可以将该Activity组件的窗口显示出来。通知应用程序进程将参数token所描述的Activity组件设置为true是通过调用AppWindowToken对象wtoken的成员函数sendAppVisibilityToClients来实现的,同时在通知之前,也会将AppWindowToken对象wtoken的成员变量clientHidden设置为false。
假设参数visible的值等于false,那么就表示要将参数token所描述的Activity组件设置不可见,这时候WindowManagerService类的成员函数setAppVisibility会继续执行以下操作:
1. 将AppWindowToken对象wtoken添加WindowManagerService类的成员变量mClosingApps所描述的一个ArrayList中,表示参数token所描述的Activity组件是正在关闭的。
2. 如果AppWindowToken对象wtoken的成员变量hidden的值等于false,那么就意味着参数token所描述的Activity组件当前是可见的。由于在这种情况下,参数token所描述的Activity组件正在等待关闭,因此,该Activity组件的窗口接下来的状态应该等待隐藏不见的,这时候就需要将AppWindowToken对象wtoken的成员变量waitingToHide的值设置为true。
这一步执行完成之后,参数token所描述的Activity组件的可见性就设置好了,回到前面的Step 3中,即ActivityStack类的成员函数realStartActivityLocked中,接下来就会通知参数token所描述的Activity组件所运行在的应用程序进程将它加载起来,并且最后调用ActivityStack类的成员函数completeResumeLocked来通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作。
接下来,我们就继续分析ActivityStack类的成员函数completeResumeLocked的实现。
Step 5. ActivityStack.completeResumeLocked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
ActivityStack {
......
private
final
void
completeResumeLocked(ActivityRecord next) {
......
// schedule an idle timeout in case the app doesn't do it for us.
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
msg.obj = next;
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
......
if
(mMainStack) {
mService.setFocusedActivityLocked(next);
}
......
ensureActivitiesVisibleLocked(
null
,
0
);
mService.mWindowManager.executeAppTransition();
......
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。
ActivityStack类的成员函数completeResumeLocked主要是执行以下三个操作:
1. 向ActivityManagerService服务所运行在的线程发送一个类型为IDLE_TIMEOUT_MSG的消息,这个消息将在IDLE_TIMEOUT毫秒后处理。这个类型为IDLE_TIMEOUT_MSG实际是用来监控WindowManagerService服务能否在IDLE_TIMEOUT毫秒之内,完成参数next所描述的Activity组件的切换操作,并且将它的窗口显示出来。如果能够到的话,WindowManagerService服务就会通知ActivityManagerService服务,然后ActivityManagerService服务就会执行一些清理工作,例如,将那些已经处于Stopped状态的Activity组件清理掉。如果不能够做到的话,那么ActivityStack类的成员函数completeResumeLocked也需要保证在IDLE_TIMEOUT毫秒之后,ActivityManagerService服务能够执行上述的清理工作。
2. 如果当前正在处理的ActivityStack对象描述的是系统当前所使用的Activity组件堆栈,即ActivityStack类的成员变量mMainStack的值等于true,那么就会调用成员变量mService所指向的一个ActivityManagerService对象的成员函数setFocusedActivityLocked来将参数next所描述的Activity组件设置为系统当前获得焦点的Activity组件。
3. 调用ActivityStack类的成员函数ensureActivitiesVisibleLocked来从上下到检查哪些Activity组件是需要设置为可见的,哪些Activity组件是需要设置为不可见的。
4. 调用成员变量mService所指向的一个ActivityManagerService对象的成员变量mWindowManager所描述的一个WindowManagerService对象的成员函数executeAppTransition来通知WindowManagerService服务执行在前面Step 2所准备好的Activity组件切换操作。
我们主要关注第3点和第4点的操作,因此,接下来我们先分析ActivityStack类的成员函数ensureActivitiesVisibleLocked的实现,然后再分析WindowManagerService类的成员函数executeAppTransition的实现。
Step 6. ActivityStack.ensureActivitiesVisibleLocked
1
2
3
4
5
6
7
8
9
10
11
|
public
class
ActivityStack {
......
final
void
ensureActivitiesVisibleLocked(ActivityRecord starting,
int
configChanges) {
ActivityRecord r = topRunningActivityLocked(
null
);
if
(r !=
null
) {
ensureActivitiesVisibleLocked(r, starting,
null
, configChanges);
}
}
......
}
|
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java。
ActivityStack类有两个重载版本的成员函数ensureActivitiesVisibleLocked,其中,两个参数版本的成员函数是通过调用四个版本的成员函数来实现的,因此,接下来我们主要分析ActivityStack类四个参数版本的成员函数ensureActivitiesVisibleLocked的实现。
ActivityStack类四个参数版本的成员函数ensureActivitiesVisibleLocked主要就是从上下到检查哪些Activity组件是需要设置为可见的,哪些Activity组件是需要设置为不可见的,我们分段来阅读:
1
2
3
|