分屏源码解析(3): 如何保证前台的两个Acitivty同时可见

一般情况下,全屏只有一个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隐藏。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容