AppTransition代表activity组件的切换过程,启动或是退出activity都会执行AppTransition,Android系统定义了多达十几种应用的transition类型,这些类型定义具体可参考WindowManager类。本文以TRANSIT_TASK_OPEN类型为例,场景以桌面点击图库冷启动为例。
以下文中AppTransition相关日志均需要执行命令开启输出到applogcat之中,正式商用版本也都可以以此方式打开:
adb shell wm logging enable-text WM_DEBUG_APP_TRANSITIONS WM_DEBUG_REMOTE_ANIMATIONS
其中,WM_DEBUG_APP_TRANSITIONS代表开启的是AppTransition相关日志;WM_DEBUG_REMOTE_ANIMATIONS表示开启的是RemoteAnimation相关的日志,桌面打开动效使用的就是RemoteAnimation。
一、冷启动跳转新应用
1、prepareAppTransition准备阶段
在startActivity阶段会调用DisplayContent.prepareAppTransition去设置AppTransition类型为TRANSIT_TASK_OPEN,执行的是图中reusedTask为空分支。
09-08 09:42:05.169 1479 5339 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.wtf.gallery3d/.app.MainActivity bnds=[48,1681][294,1992] (has extras)} from uid 10100
09-08 09:42:05.179 1479 5339 V WindowManager: Prepare app transition: transit=TRANSIT_TASK_OPEN mNextAppTransition=TRANSIT_UNSET alwaysKeepCurrent=false displayId=0 Callers=com.android.server.wm.DisplayContent.prepareAppTransition:5168 com.android.server.wm.DisplayContent.prepareAppTransition:5162 com.android.server.wm.ActivityStack.startActivityLocked:2414 com.android.server.wm.ActivityStarter.startActivityInner:2246 com.android.server.wm.ActivityStarter.startActivityUnchecked:1987
Prepare app transition日志中的transit是打算要设置的新transit,mNextAppTransition是当前已经存在的transit。
该函数功能如下:
1)设置mNextAppTransition表明要执行的activity组件切换类型,设置值(非TRANSIT_UNSET值)后使得AppTransition.isTransitionSet()返回true表面已经设置了AppTransition。
2)该函数若重复执行,按其内部逻辑存在覆盖和不覆盖两种场景:覆盖场景常见的比如TRANSIT_TASK_OPEN替换掉TRANSIT_TASK_CLOSE、TRANSIT_ACTIVITY_OPEN 替换掉TRANSIT_ACTIVITY_CLOSE,也就是常说的open activity和open task的优先级要高于close activity和close task;不覆盖场景比如热启应用复用栈时会出现activityPaused阶段执行到resumeTop去设置TRANSIT_TASK_OPEN尝试替换掉startActivity阶段设置的TRANSIT_TASK_TO_FRONT,但不会被替换成功。
3)执行AppTransition.prepare()函数设置mAppTransitionState为APP_STATE_IDLE,即设置AppTransition的状态为IDLE空闲态,因为只有空闲状态才能执行下一个即将要执行的AppTransition。
完成这一步表明App Transition准备完成,但离动画执行还有十万八千里。
其实在activityPaused阶段去resumeTop时仍然会触发一次设置TRANSIT_TASK_OPEN,代码和日志如下:
09-08 09:42:05.191 1479 1495 V WindowManager: Prepare app transition: transit=TRANSIT_TASK_OPEN mNextAppTransition=TRANSIT_TASK_OPEN alwaysKeepCurrent=false displayId=0 Callers=com.android.server.wm.DisplayContent.prepareAppTransition:5168 com.android.server.wm.DisplayContent.prepareAppTransition:5162 com.android.server.wm.ActivityStack.resumeTopActivityInnerLocked:2059 com.android.server.wm.ActivityStack.resumeTopActivityUncheckedLocked:1710 com.android.server.wm.RootWindowContainer.resumeFocusedStacksTopActivities:2478
activityPaused阶段的resumeTop设置动效类型为TRANSIT_TASK_OPEN,针对startActivity场景其实该处设置无效,因为之前startActivity流程已经设置过,但是对于back键等途径触发的resumeTop场景的设置仍然有效。
2、setVisibility阶段
ActivityRecord.setVisibility函数的主要功能是把设置true的ActivityRecord加到DisplayContent.mOpeningApps列表,把设置false的ActivityRecord加到DisplayContent.mClosingApps列表,该函数在每次启动页面(不论热启冷起)的整个过程中可能涉及多次调用,是可重入的,且在执行DisplayContent.executeAppTransition之前都不会真正的commitVisibility(GOOD TO GO阶段才会真正执行),仅仅是填充mOpeningApps和mClosingApps,目的是在GOOD TO GO真正做动效时有目标的退场和入场应用去执行动效。
冷起时首次触发桌面加到mClosingApps、图库加到mOpeningApps是由activityPaused阶段的以下函数触发:
之前分析应用启动时曾说过,冷起新栈,由completePaused结束的那次ensureActivitiesVisible来触发将桌面makeInvisible进而触发ActivityRecord.setVisibility(false)加到mClosingApps、将图库makeVisibleAndRestartIfNeeded进而触发ActivityRecord.setVisibility(true)加到mOpeningApps。
图库ensure执行的分支:
09-08 09:42:05.192 1479 1495 V WindowManager: setAppVisibility(Token{5817814 ActivityRecord{7ddd3b9 u0 com.wtf.gallery3d/.app.MainActivity t5931}}, visible=true): mNextAppTransition=TRANSIT_TASK_OPEN visible=false mVisibleRequested=false Callers=com.android.server.wm.ActivityRecord.setVisibility:4405 com.android.server.wm.EnsureActivitiesVisibleHelper.makeVisibleAndRestartIfNeeded:223 com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState:155 com.android.server.wm.EnsureActivitiesVisibleHelper.lambda$Bbb3nMFa3F8er_OBuKA7-SpeSKo:0 com.android.server.wm.-$$Lambda$EnsureActivitiesVisibleHelper$Bbb3nMFa3F8er_OBuKA7-SpeSKo.accept:12 com.android.internal.util.function.pooled.PooledLambdaImpl.doInvoke:307
桌面ensure执行的分支:
09-08 09:42:05.192 1479 1495 V WindowManager: setAppVisibility(Token{6d196ad ActivityRecord{e45e1e5 u0 com.wtf.launcher/.Launcher t5815}}, visible=false): mNextAppTransition=TRANSIT_TASK_OPEN visible=true mVisibleRequested=true Callers=com.android.server.wm.ActivityRecord.setVisibility:4405 com.android.server.wm.ActivityRecord.makeInvisible:5168 com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState:182 com.android.server.wm.EnsureActivitiesVisibleHelper.lambda$Bbb3nMFa3F8er_OBuKA7-SpeSKo:0 com.android.server.wm.-$$Lambda$EnsureActivitiesVisibleHelper$Bbb3nMFa3F8er_OBuKA7-SpeSKo.accept:12 com.android.internal.util.function.pooled.PooledLambdaImpl.doInvoke:307
虽然activityPaused中的completePaused可以最终触发到桌面和图库的ActivityRecord.setVisibility,但由于是冷起栈,realStartActivityLocked阶段仍然可以触发要启动的图库执行一次ActivityRecord.setVisibility(true),且该次才是最关键的一次,日志如下:
09-08 09:42:05.235 1479 4303 V WindowManager: setAppVisibility(Token{5817814 ActivityRecord{7ddd3b9 u0 com.wtf.gallery3d/.app.MainActivity t5931}}, visible=true): mNextAppTransition=TRANSIT_TASK_OPEN visible=false mVisibleRequested=true Callers=com.android.server.wm.ActivityRecord.setVisibility:4405 com.android.server.wm.ActivityStackSupervisor.realStartActivityLocked:881 com.android.server.wm.RootWindowContainer.startActivityForAttachedApplicationIfNeeded:2139 com.android.server.wm.RootWindowContainer.lambda$5fbF65VSmaJkPHxEhceOGTat7JE:0 com.android.server.wm.-$$Lambda$RootWindowContainer$5fbF65VSmaJkPHxEhceOGTat7JE.apply:8 com.android.internal.util.function.pooled.PooledLambdaImpl.doInvoke:315