一般情况下,全屏只有一个activity,在前台的状态为resumed,在后台的均为stopped,一般认为stop就不可见了。但是从framework的角度来看并非如此,不是stop就不可见了,activity的生命周期与它画面的可见性并没有必然关系(除非destroy了,那么画面肯定也要消失了),分屏模式中有两个stack在前台,但并非两个stack中的acitivty的生命周期都为resumed,而是一个resumed,一个pasued 或者 stopped,那么如何保证可见性呢?
一个activity对应一个AMS中的ActivityRecord,其中有两个变量:
boolean visible; // does this activity's window need to be shown?
boolean nowVisible; // is this activity's window visible?
可以通过如下命令查看它们的值:dumpsys activity a,该命令可以dump出来所有的stack和所包含的task的信息,实现在AMS的dump函数中。
假设现在前台处于分屏模式有两个activity,左边的为docked stack,前台activity为1,右边为recent stack,前台activity为2,中间的圆圈为DividerView,它是用于拖拽从而改变的栈的边界大小的,拖出一定范围就可以显示为全屏,如下:
现在假设DOCKED stack中的activity为paused状态,右边则为resumed,点击一下DOCKED stack的屏幕区域,activity1会resume,activity2则会进入paused状态,此时会进入进入一个判断是否可见的函数,该函数位于ActivityStackSupervisor类中:
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
mKeyguardController.beginActivityVisibilityUpdate();
try {
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
final int topStackNdx = stacks.size() - 1;
for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows); //starting,就是我们要resume的 activity1
}
}
} finally {
mKeyguardController.endActivityVisibilityUpdate();
}
}
从ActivityDisplay中取到所有stack(一般只有一个Display,也就是id为0的display,目前先只考虑这种情况,这里的包含关系可以看分屏源码解析(1))遍历所有的stack,然后调用它们的ensureActivitiesVisibleLocked函数,判断其他的stack的activity是否可见:
/**
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
*/
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
mTopActivityOccludesKeyguard = false;
mTopDismissingKeyguardActivity = null;
mStackSupervisor.mKeyguardController.beginActivityVisibilityUpdate();
try {
ActivityRecord top = topRunningActivityLocked();
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
+ " configChanges=0x" + Integer.toHexString(configChanges));
if (top != null) {
checkTranslucentActivityWaiting(top);
}
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
final int stackVisibility = shouldBeVisible(starting); //这个栈是否应该可见,有VISIBLE和INVISIBLE
final boolean stackInvisible = stackVisibility != STACK_VISIBLE; //转为boolean值
final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
boolean behindFullscreenActivity = stackInvisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
boolean behindTranslucentActivity = false;
final ActivityRecord visibleBehind = getVisibleBehindActivity();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { //遍历这个栈中的所有task
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { //遍历这个栈中的某个task的所有 activity
final ActivityRecord r = activities.get(activityNdx);
if (r.finishing) {
// Normally the screenshot will be taken in makeInvisible(). When an activity
// is finishing, we no longer change its visibility, but we still need to take
// the screenshots if startPausingLocked decided it should be taken.
if (r.mUpdateTaskThumbnailWhenHidden) {
r.updateThumbnailLocked(r.screenshotActivityLocked(),
null /* description */);
r.mUpdateTaskThumbnailWhenHidden = false;
}
continue;
}
final boolean isTop = r == top;
if (aboveTop && !isTop) {
continue;
}
aboveTop = false;
// Check whether activity should be visible without Keyguard influence
final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard( //判断keyguard可见的情况,先忽略
behindTranslucentActivity, stackVisibleBehind, visibleBehind,
behindFullscreenActivity);
r.visibleIgnoringKeyguard = visibleIgnoringKeyguard;
// Now check whether it's really visible depending on Keyguard state.
boolean reallyVisible = checkKeyguardVisibility(r,
visibleIgnoringKeyguard, isTop); //该boolean综合了之前的情况,然后决定某个activity是否应该可见
Slog.d("ActivityStack", "guanwenreallyVisible:" + visibleIgnoringKeyguard + " stack:" + r.getStackId() + " r:" + r);
if (visibleIgnoringKeyguard) {
behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
behindFullscreenActivity, task, r);
if (behindFullscreenActivity && !r.fullscreen) {
behindTranslucentActivity = true;
}
}
if (reallyVisible) { //可见,那么就让它变为visible
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "guanwen Make visible? " + r
+ " finishing=" + r.finishing + " state=" + r.state + " stack:" + r.getStackId());
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindows);
}
if (r.app == null || r.app.thread == null) { //应用不存在但是又可见,重新拉起
if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeNextActivity, r)) {
if (activityNdx >= activities.size()) {
// Record may be removed if its process needs to restart.
activityNdx = activities.size() - 1;
} else {
resumeNextActivity = false;
}
}
} else if (r.visible) { //visible的值已经为true了,不用重复设置
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Skipping: already visible at " + r);
if (r.handleAlreadyVisible()) {
resumeNextActivity = false;
}
} else {
r.makeVisibleIfNeeded(starting); //使该activity可见
}
// Aggregate current change flags.
configChanges |= r.configChangeFlags;
} else { //reallyVisible为false,该activity为invisible
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
+ stackInvisible + " behindFullscreenActivity="
+ behindFullscreenActivity + " mLaunchTaskBehind="
+ r.mLaunchTaskBehind + r.getStackId());
makeInvisible(r, visibleBehind); //使该activity不可见
}
}
if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
} else if (mStackId == HOME_STACK_ID) {
if (task.isHomeTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// No other task in the home stack should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
// them. However, when they don't have the wallpaper behind them, we want to
// show activities in the next application stack behind them vs. another
// task in the home stack like recents.
behindFullscreenActivity = true;
} else if (task.isRecentsTask()
&& task.getTaskToReturnTo() == APPLICATION_ACTIVITY_TYPE) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Recents task returning to app: at " + task
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// We don't want any other tasks in the home stack visible if the recents
// activity is going to be returning to an application activity type.
// We do this to preserve the visible order the user used to get into the
// recents activity. The recents activity is normally translucent and if it
// doesn't have the wallpaper behind it the next activity in the home stack
// shouldn't be visible when the home stack is brought to the front to display
// the recents activity from an app.
behindFullscreenActivity = true;
}
}
}
if (mTranslucentActivityWaiting != null &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
// Nothing is getting drawn or everything was already visible, don't wait for timeout.
notifyActivityDrawnLocked(null);
}
} finally {
mStackSupervisor.mKeyguardController.endActivityVisibilityUpdate();
}
}
该函数很长,总结一下:
1.判断该stack的可见性,如果stack不可见,里面所有的activity也是不可见的。有如下case:
// Stack is not considered visible.
static final int STACK_INVISIBLE = 0; //该stack不可见
// Stack is considered visible
static final int STACK_VISIBLE = 1; //可见
// Stack is considered visible, but only becuase it has activity that is visible behind other
// activities and there is a specific combination of stacks.
static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2; //该stack包含了一个可见的activity,认为可见,但它位于其他可见activity的后面
2.遍历该stack的所有task,然后再依次遍历某个task的所有activity,源码里有其他的case,但是我们只考虑分屏的情况下,stack可见,那么该stack中的activity就是可见的,所以会走进reallyVisible为true的情况,否则走false的情况。
3.如果为true,那么先判断该activity的进程是否为空(比如刚开机,就打开recent想去分屏,这个时候进程其实被杀了,会走这里),然后判断是否已经可见了,最后调用makeVisibleIfNeeded(),这里会让activity可见,怎么做的稍后谈。
所以stack的可见性的判断是十分重要的,这决定着是否会调用makeBisibleIfNeed()是否会被调用。可见性的判断很多,也有优先级,函数如下:
/**
* Returns what the stack visibility should be: {@link #STACK_INVISIBLE}, {@link #STACK_VISIBLE}
* or {@link #STACK_VISIBLE_ACTIVITY_BEHIND}.
*
* @param starting The currently starting activity or null if there is none.
*/
int shouldBeVisible(ActivityRecord starting) {
if (!isAttached() || mForceHidden) { //该stack为null,或者ForceHidden被设置为true,返回不可见
return STACK_INVISIBLE;
}
if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) { //该stack在栈顶或者为focused stack,返回可见
return STACK_VISIBLE;
}
final int stackIndex = mStacks.indexOf(this); //获取该stack在栈中的位置(就是一个数组,获取它的小标,不过是按栈的方式去管理)
if (stackIndex == mStacks.size() - 1) { //如果该栈没位于栈顶但是处于的位置又是在栈的最后面,返回不可见
Slog.wtf(TAG,
"Stack=" + this + " isn't front stack but is at the top of the stack list");
return STACK_INVISIBLE;
}
// Check position and visibility of this stack relative to the front stack on its display.
final ActivityStack topStack = getTopStackOnDisplay();
final int topStackId = topStack.mStackId;
if (StackId.isBackdropToTranslucentActivity(mStackId)
&& hasVisibleBehindActivity() && StackId.isHomeOrRecentsStack(topStackId)
&& (topStack.topActivity() == null || !topStack.topActivity().fullscreen)) {
// The fullscreen or assistant stack should be visible if it has a visible behind
// activity behind the home or recents stack that is translucent.
return STACK_VISIBLE_ACTIVITY_BEHIND;
}
if (mStackId == DOCKED_STACK_ID) { //该栈为DOCKED stack,那么该栈总是可见的
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isAssistantStack()) {
return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
: STACK_INVISIBLE;
}
return STACK_VISIBLE;
}
// Set home stack to invisible when it is below but not immediately below the docked stack
// A case would be if recents stack exists but has no tasks and is below the docked stack
// and home stack is below recents
if (mStackId == HOME_STACK_ID) { //如果该栈为HOME stack,但是DOCKED stack位于它的上面,且该栈不被DOCKED stack直接压着,返回不可见
int dockedStackIndex = mStacks.indexOf(mStackSupervisor.getStack(DOCKED_STACK_ID));
if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
return STACK_INVISIBLE;
}
}
// Find the first stack behind front stack that actually got something visible.
int stackBehindTopIndex = mStacks.indexOf(topStack) - 1; //在栈顶下面的第二个栈的index,初始值为栈顶的index
while (stackBehindTopIndex >= 0 &&
mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) { //如果top activity为null,继续找下一个
stackBehindTopIndex--;
}
final int stackBehindTopId = (stackBehindTopIndex >= 0) 位于该栈下面的某个top activity不为null的栈的id
? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID) //栈顶为DOCKED stack
&& (stackIndex == stackBehindTopIndex //该栈就是在栈顶下的第二个top activity不为null的stack
|| ((stackBehindTopId == DOCKED_STACK_ID) //或者在栈顶下的第二个top activity不为null的stack为DOCKED stack
&& stackIndex == stackBehindTopIndex - 1))) { //这里很重要,单独说!!!!!!!!!!!!!!!!!!!!
// Stacks directly behind the docked or pinned stack are always visible.
// Also this stack is visible if behind docked stack and the docked stack is behind the
// top-most pinned stack
return STACK_VISIBLE;
}
if (StackId.isBackdropToTranslucentActivity(topStackId)
&& topStack.isStackTranslucent(starting, stackBehindTopId)) {
// Stacks behind the fullscreen or assistant stack with a translucent activity are
// always visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
if (stackIndex == stackBehindTopIndex) {
return STACK_VISIBLE;
}
if (stackBehindTopIndex >= 0) {
if ((stackBehindTopId == DOCKED_STACK_ID
|| stackBehindTopId == PINNED_STACK_ID)
&& stackIndex == (stackBehindTopIndex - 1)) {
// The stack behind the docked or pinned stack is also visible so we can have a
// complete backdrop to the translucent activity when the docked stack is up.
return STACK_VISIBLE;
}
}
}
if (StackId.isStaticStack(mStackId)) { //如果为静态栈,默认不可见
// Visibility of any static stack should have been determined by the conditions above.
return STACK_INVISIBLE;
}
for (int i = stackIndex + 1; i < mStacks.size(); i++) {
final ActivityStack stack = mStacks.get(i);
if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
continue;
}
if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) { //不允许动态栈在后台可见,返回不可见
// These stacks can't have any dynamic stacks visible behind them.
return STACK_INVISIBLE;
}
if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
return STACK_INVISIBLE;
}
}
return STACK_VISIBLE; //可见
}
情况很多,但是我们分屏着重需要考虑的就几个:
1.首先,focused stack或者位于栈顶的,肯定可见,这个没什么问题
2.如果DOCKED stack存在,那么自然也应该可见。
3.在代码中标记单独说的case,是前台能看见两个stack的关键,假设现在recent stack对象走到了该函数,在我们假定的点击DOCKED stack区域,该stack的activity会resume从而使得DOCKED stack位于栈顶,而recent stack则处于栈顶下面第二个位置,此时recent stack是否可见就会走进这里:栈顶是DOCKED stack,而recent stack的top activitity并不为null,所以此时的stackBehindTopIndex就是栈顶的位置,减一就是recent stack的位置,如此recent stack就能通过这些判断条件,从而返回了可见的值,因此recent stack就变得可见了!
下图就是栈的结构,结合3的情况考虑,栈的可见性对于分屏来说很重要,必须理解,我们的目的就是让前台的两个栈可见,所以得让栈顶的两个栈都为Visible.
stack可见后,会调用ActivityRecord的makeVisibleIfNeed():
void makeVisibleIfNeeded(ActivityRecord starting) {
// This activity is not currently visible, but is running. Tell it to become visible.
if (state == RESUMED || this == starting) {
if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY,
"Not making visible, r=" + this + " state=" + state + " starting=" + starting);
return;
}
// If this activity is paused, tell it to now show its window.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Making visible and scheduling visibility: " + this);
final ActivityStack stack = getStack();
try {
if (stack.mTranslucentActivityWaiting != null) {
updateOptionsLocked(returningOptions);
stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
}
setVisible(true); //设置visible为true
sleeping = false;
app.pendingUiClean = true;
app.thread.scheduleWindowVisibility(appToken, true /* showWindow */); //通知客户端show window
// The activity may be waiting for stop, but that is no longer appropriate for it.
mStackSupervisor.mStoppingActivities.remove(this);
mStackSupervisor.mGoingToSleepActivities.remove(this);
} catch (Exception e) {
// Just skip on any failure; we'll make it visible when it next restarts.
Slog.w(TAG, "Exception thrown making visibile: " + intent.getComponent(), e);
}
handleAlreadyVisible();
}
setVisible函数中会将visible标志位置为true,然后触发WMS中的逻辑,这个暂且不看,通知客户端的函数为scheduleWindowVisibility(),它会发送给客户端的主线程 SHOW_WINDOW的消息,然后会调用到ActivityThread中的updateVisibility(),最终调用到某个activity的makeVisible:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
到这里就清晰了,会让activity的顶层view变得可见,如此一来,activity的生命周期哪怕进入了stopped,也能变得可见。
总结:
如果stack是可见的,那么其中的activity也是可见的,做多分屏的时候,我们可以通过一些策略让stack变得可见,从而不让activity隐藏。