忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
-- 服装学院的IT男
上面一篇主要是 launcher 部分的处理,已经找到 launcher 传递过来的 RemoteAnimationAdapter 被赋值到了 RemoteAnimationController 对象选 mRemoteAnimationController 中。 这篇主要是对 system_server 进程中 AppTransition 的简单介绍和动画过程相关的 AppTransition 的处理。
1 AppTransition简介
从代码上看 AppTransition 是一个java类,也是是在 DisplayContent 的一个成员变量代码如下:
# DisplayContent
final AppTransition mAppTransition;
DisplayContent(Display display, RootWindowContainer root) {
......
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
......
}
因此可以确定一个“屏幕”只有一个 AppTransition 对象。
从业务逻辑来看 AppTransition 代表的是一个 Activity 的切换过程,比如屏幕解锁也是一个 AppTransition ,当前桌面冷启动一个应用最终也是要显示一个 Activity ,所以也是一次 AppTransition。
应用内打开 Activity 也是一次切换过程,可以理解为屏幕上有 Activity 的变化就是一次 AppTransition 。
1.1 AppTransition 的类型
google 定义了一组 TransitionType 来表示具体的 ctivity 的切换场景,这些值被定义在 WindowManager 下。
# WindowManager
/** @hide */
int TRANSIT_NONE = 0;
/**
* A window that didn't exist before has been created and made visible.
* @hide
* 一个以前不存在的窗口已经创建并可见
*/
int TRANSIT_OPEN = 1;
/**
* A window that was visible no-longer exists (was finished or destroyed).
* @hide
* 可见的窗口变得不可以(关闭)
*/
int TRANSIT_CLOSE = 2;
/**
* A window that already existed but was not visible is made visible.
* @hide
* 已存在但不可见的窗口变为可见 (按home键回到launcher)
*/
int TRANSIT_TO_FRONT = 3;
......
int TRANSIT_FIRST_CUSTOM = 12;
目前一共是定义了13个类型。 当前的类型就是 TRANSIT_OPEN ,表示一个新 Activity 的显示。这个在上一篇其实也看到了, 在 TaskFragment::resumeTopActivity 方法内调用 DisplayContent::prepareAppTransition 传递的就是这个 TRANSIT_OPEN 。
用户在不同的场景下操作后会触发 Activity 的改变,就有对应 TransitionType ,那这些 type 怎么用呢?
在 AppTransition 下有一个集合,维护着 TransitionType ,当有 Activity 切换的时候,就会触发把对应的 TransitionType 添加到这个集合中。然后在适当的时机也有进行移除。
# AppTransition
// 下一个切换事件的请求类型
private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
1.2 AppTransition 对应的事件处理流程概览
对于一个 AppTransition 的事件处理,一般可以分为以下3大流程:
-
prepareAppTransition 流程
这一步主要是让集合中添加响应的 TransitionType 。 我们知道 AppTransition 代表这一个 Activity 的转换,触发场景自然也有多个,所以流程中如果会触发 Activity 的改变就会调用 AppTransition::prepareAppTransition 传递一个当前场景的 TransitionType 到 mNextAppTransitionRequests 集合中。
-
setReady (executeAppTransition)流程
这一步的目的是表示本次 AppTransition 的 TransitionType 收集完了,可以准备开始动画了。
会触发这一步的地方逻辑也有多个,但是本质是一样的。既然要做 Activity 切换动画,自然是新的 Activity 有内容,或者马上就要有内容的时候,就可以准备开始了。
为什么说马上要有内容也可以呢?因为这一步只是表示准备好可以动画,还不是真正的动画。真正的动画播放前肯定是要有窗口内容的,但是这一步还可以不需要有窗口内容。
所以当要启动 Activity 的时候也会执行这一步,因为 Activity 启动后不久就是窗口三部曲,所以这个时候标记为准备好做动画也是可以的。
当然还要先判断下 mNextAppTransitionRequests 中是否有数据,有则 AppTransition::isTransitionSet 返回 true 才执行 AppTransition::setReady 方法,把 AppTransition 下的 mAppTransitionState 变量设置为 APP_STATE_READY 状态 。
-
goodToGo流程
到了这一步,就是一切准备就绪了,可以执行 Activity 的转换动画了。 进入的触发是一次 “layout” ,如果已经执行过 AppTransition::setReady 那么 AppTransition::isReady 就会返回 true ,当然真正要播放动画还需要窗口的状态满足播放动画的条件, 这一步是在 AppTransitionController::transitionGoodToGo 中控制的。
真正进入 goodToGo 流程后会做2件事:1. 创建leash图层。 2. 触发 Activity 转换的动画执行。
2. 动画流程
前面简单介绍了 AppTransition 的概念以及工作流程,下面回到启动应用的动画场景看一下 AppTransition 是如何处理的。
2.1 AppTransition::prepareAppTransition 流程
# AppTransition
public class AppTransition implements Dump {
// 下一个切换事件的请求类型
private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
}
// 将事件添加到集合中
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
......
// 将transit添加进mNextAppTransitionRequests
mNextAppTransitionRequests.add(transit);
......
return prepare();
}
// 根据集合中是否有数据确定是有 AppTransition事件需要处理
boolean isTransitionSet() {
return !mNextAppTransitionRequests.isEmpty();
}
这一阶段是收集 AppTransition 事件的动作,也就是用户的操作如果会涉及到 Activity 切换就会执行到这个方法,将对应的 TransitionType 添加到集合中。
这里还有有个 isTransitionSet 方法也很重要,返回值是 mNextAppTransitionRequests 集合是否有值。
在代码的执行逻辑里,会根据这个方法的返回值来判断当前是否有 AppTransition 相关的逻辑处理。
当前分析的场景下会执行2次 prepareAppTransition 。
2.1.1 AppTransition::prepareAppTransition 第一次执行
AppTransition 既然是表示一次 Activity 切换,那启动一个 Activity 肯定会有一次 prepareAppTransition 。所以第一次执行就是在 Activity 启动流程的初期,在 ActivityTaskManagerService::startActivity 方法执行到 ActivityRecord 创建的时候。回顾一下之前整理的调用链:
ActivityTaskManagerService::startActivity
ActivityTaskManagerService::startActivityAsUser
ActivityTaskManagerService::startActivityAsUser
ActivityStartController::obtainStarter
ActivityStarter::execute
ActivityStarter::executeRequest -- 构建 ActivityRecord
ActivityStarter::startActivityUnchecked
ActivityStarter::startActivityInner -- 关键函数startActivityInner
ActivityStarter::getOrCreateRootTask -- 创建或者拿到Task
ActivityStarter::setNewTask -- 将task与activityRecord 绑定
RootWindowContainer::resumeFocusedTasksTopActivities -- 显示Activity
主要关注 ActivityStarter::startActivityInner ,这个方法在Activity启动流程已经看过了,所以就忽略掉多余代码,只保留主要调用。
# ActivityStarter
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord ......) {
......
if (mTargetRootTask == null) {
// 重点* 1. 创建Task
mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
mOptions);
}
......
// 重点* 2. 将需要启动的ActivityRecord与 新创建的Task 进行绑定
setNewTask(taskToAffiliate);
......
// 重点* 3 StartWindow流程
mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
mOptions, sourceRecord);
......
if (mDoResume) {
......
// 重点* 4 task 处理完后,需要将task顶部的Activity显示(resume)
mRootWindowContainer.resumeFocusedTasksTopActivities(
mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
}
......
}
其中和本次动画分析的 AppTransition::prepareAppTransition 流程在第3点,这里也后面也会触发 StartWindow 的创建。
# Task
void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
......
final DisplayContent dc = mDisplayContent;
......
// 触发一次prepareAppTransition
dc.prepareAppTransition(TRANSIT_OPEN);
......
if (r.mLaunchTaskBehind) {
......
} else if (SHOW_APP_STARTING_PREVIEW && doShow) {
......
Task baseTask = r.getTask();
......
// StartWindow 相关
final ActivityRecord prev = baseTask.getActivity(
a -> a.mStartingData != null && a.showToCurrentUser());
mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
isTaskSwitch, sourceRecord);
}
......
}
这里看到执行了 DisplayContent::prepareAppTransition 传递了 TRANSIT_OPEN 类型的 TransitionType 。 这个还是很好理解的,当前是启动 Activity 流程,那添加一个 TRANSIT_OPEN 类型自然是应该。 这个方法后续会把这个TRANSIT_OPEN保存到 AppTransition 的 mNextAppTransitionRequests 下,后续代码如下:
# DisplayContent
final AppTransition mAppTransition;
void prepareAppTransition(@WindowManager.TransitionType int transit) {
prepareAppTransition(transit, 0 /* flags */);
}
void prepareAppTransition(@WindowManager.TransitionType int transit,
@WindowManager.TransitionFlags int flags) {
// 执行 AppTransition::prepareAppTransition
final boolean prepared = mAppTransition.prepareAppTransition(transit, flags);
if (prepared && okToAnimate() && transit != TRANSIT_NONE) {
mSkipAppTransitionAnimation = false;
}
}
集合上面补充这块的逻辑,完整的调用链就是下面这样了:
ActivityTaskManagerService::startActivity
ActivityTaskManagerService::startActivityAsUser
ActivityTaskManagerService::startActivityAsUser
ActivityStartController::obtainStarter
ActivityStarter::execute
ActivityStarter::executeRequest -- 构建 ActivityRecord
ActivityStarter::startActivityUnchecked
ActivityStarter::startActivityInner -- 关键函数startActivityInner
ActivityStarter::getOrCreateRootTask -- 创建或者拿到Task
ActivityStarter::setNewTask -- 将task与activityRecord 绑定
Task::startActivityLocked -- StartWindow相关
DisplayContent::prepareAppTransition
DisplayContent::prepareAppTransition
AppTransition::prepareAppTransition -- 添加 TRANSIT_OPEN
StartingSurfaceController::showStartingWindow
RootWindowContainer::resumeFocusedTasksTopActivities -- 显示Activity
2.1.2 AppTransition::prepareAppTransition 第二次执行
在上一篇看解析 RemoteAnimationAdapter 的流程的时候也看到执行了一次 DisplayContent::prepareAppTransition ,这次就是第二次执行。
回顾一下上一篇的调用链,其中加了 * 号的是和 prepareAppTransition 流程相关的。
ActivityClientController::activityPaused
ActivityRecord::activityPaused
TaskFragment::completePause
RootWindowContainer::resumeFocusedTasksTopActivities
RootWindowContainer::resumeFocusedTasksTopActivities
Task::resumeTopActivityUncheckedLocked
Task::resumeTopActivityInnerLocked
TaskFragment::resumeTopActivity
DisplayContent::prepareAppTransition*
AppTransition::prepareAppTransition* -- 将TRANSIT_OPEN这个transit添加进mNextAppTransitionRequests
ActivityRecord::applyOptionsAnimation*
AppTransition::overridePendingAppTransitionRemote*
AppTransition::overridePendingAppTransitionRemote* -- 对mRemoteAnimationController赋值
RemoteAnimationController::init * -- 远端动画的adapter保存在这
ActivityTaskSupervisor::startSpecificActivity -- 尝试启动 Activity
这个调用链是 SourceActivity 触发的 activityPaused 流程。 在这里触发 prepareAppTransition 流程的原因我的理解是:一个 Activity 执行了 pause,那也有一个新的 Activity 显示。 所以也会添加一个 TRANSIT_OPEN。
继续回顾一下 TaskFragment::resumeTopActivity 方法:
# TaskFragment
final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
ActivityRecord next = topRunningActivity(true /* focusableOnly */);
......
// 处理activity的pause流程
boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
......
// 默认需要动画
boolean anim = true;
......
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
// 重点* 1. 触发AppTransition::prepareAppTransition
dc.prepareAppTransition(TRANSIT_OPEN,
next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
}
......
if (anim) {
// 重点* 2. 当前场景 anim为true,这里使用launcher带了的adapter构建RemoteAnimationController
next.applyOptionsAnimation();
} else {
next.abortAndClearOptionsAnimation();
}
......
// 重点* 3. 启动 Activity 流程
mTaskSupervisor.startSpecificActivity(next, true, true);
}
这里有3个重点本次看的就是执行 DisplayContent::prepareAppTransition 。 后续的代码和上一小节一样就不看了。
这里可能会有一个疑惑点,怎么加了2次 TRANSIT_OPEN 到集合中,这个其实没啥问题,不影响后续的执行。 而且其他场景在 prepareAppTransition 阶段可能会添加更多的 TransitionType 到集合里,但是在最后取出来执行业务逻辑的时候 AppTransition 是有一套自己的规则的。
现在知道完整的执行了 AppTransition::prepareAppTransition 那么 mNextAppTransitionRequests 数组就有值,AppTransition::isTransitionSet 就会返回 true
2.2 AppTransition::setReady -- executeAppTransition 流程
上一小节看到 TargetActivity 启动和 SourceActivity pause的流程中都执行了 AppTransition::prepareAppTransition 方法并传递了 TRANSIT_OPEN 的 TransitionType 到 AppTransition 下的 mNextAppTransitionRequests 集合下。
虽然下一步的流程可以叫为 executeAppTransition 流程,看名字似乎就是要执行 AppTransition 了,但是真正的执行并不是在这次,而且这个 executeAppTransition 方法其实是定义在 DisplayContent 下的, DisplayContent::executeAppTransition 方法中调用的是 AppTransition::setReady 。所以这一阶段对于 AppTransition 来说,只是“设置状态为准备好执行了”。
先看 AppTransition::isReady 方法
2.2.1 AppTransition::isReady
# AppTransition
// 空闲
private final static int APP_STATE_IDLE = 0;
// 准备
private final static int APP_STATE_READY = 1;
// 执行中
private final static int APP_STATE_RUNNING = 2;
// 超时
private final static int APP_STATE_TIMEOUT = 3;
// 当前状态
private int mAppTransitionState = APP_STATE_IDLE;
boolean isReady() {
return mAppTransitionState == APP_STATE_READY
|| mAppTransitionState == APP_STATE_TIMEOUT;
}
// set方法
void setReady() {
// 堆栈
android.util.Log.e("biubiubiu", "AppTransition setReady: "+this, new Exception());
setAppTransitionState(APP_STATE_READY);
fetchAppTransitionSpecsFromFuture();
}
-
- 定义了4个状态
-
- APP_STATE_TIMEOUT 是超时的异常状态,这种场景暂不考虑
-
- 当状态为 APP_STATE_READY 的时候,AppTransition::isReady 返回 true
-
- 唯一设置 APP_STATE_READY 状态的方法是 AppTransition::setReady
虽然这里对于 AppTransition::isReady 的介绍不多,但是这个方法极其重要,它是用来控制当前逻辑是否需要有 AppTransition 要执行的关键。
我们执行屏幕的 “layout” 流程调用的很频繁,很多逻辑都是在“layout” 流程触发的,AppTransition 也是一样。
“layout” 流程会触发 RootWindowContainer::checkAppTransitionReady 方法,只有当 AppTransition::isReady 返回 true 才会进入到后续的 AppTransitionController::handleAppTransitionReady 等流程
所以 AppTransition::isReady 就是控制 AppTransition 是否要执行的关键。
2.2.2 DisplayContent::executeAppTransition
这小节看一下是什么逻辑执行下来最终会调用 AppTransition::setReady 方法。
我在 AppTransition::setReady 方法加的堆栈打印如下
E biubiubiu: AppTransition setReady: mNextAppTransitionRequests=[TRANSIT_OPEN, TRANSIT_OPEN], mNextAppTransitionFlags=
E biubiubiu: java.lang.Exception
E biubiubiu: at com.android.server.wm.AppTransition.setReady(AppTransition.java:281)
E biubiubiu: at com.android.server.wm.DisplayContent.executeAppTransition(DisplayContent.java:5376)
E biubiubiu: at com.android.server.wm.RootWindowContainer.executeAppTransitionForAllDisplay(RootWindowContainer.java:2190)
E biubiubiu: at com.android.server.wm.ActivityTaskSupervisor.reportResumedActivityLocked(ActivityTaskSupervisor.java:1890)
E biubiubiu: at com.android.server.wm.ActivityRecord.completeResumeLocked(ActivityRecord.java:6033)
E biubiubiu: at com.android.server.wm.Task.minimalResumeActivityLocked(Task.java:4833)
E biubiubiu: at com.android.server.wm.ActivityTaskSupervisor.realStartActivityLocked(ActivityTaskSupervisor.java:976)
E biubiubiu: at com.android.server.wm.RootWindowContainer$AttachApplicationHelper.test(RootWindowContainer.java:3616)
E biubiubiu: at com.android.server.wm.RootWindowContainer$AttachApplicationHelper.test(RootWindowContainer.java:3566)
E biubiubiu: at com.android.server.wm.ActivityRecord.forAllActivities(ActivityRecord.java:4560)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllActivities(WindowContainer.java:1657)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllActivities(WindowContainer.java:1651)
E biubiubiu: at com.android.server.wm.RootWindowContainer$AttachApplicationHelper.accept(RootWindowContainer.java:3604)
E biubiubiu: at com.android.server.wm.RootWindowContainer$AttachApplicationHelper.accept(RootWindowContainer.java:3566)
E biubiubiu: at com.android.server.wm.Task.forAllRootTasks(Task.java:3196)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
E biubiubiu: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2026)
E biubiubiu: at com.android.server.wm.RootWindowContainer$AttachApplicationHelper.process(RootWindowContainer.java:3582)
E biubiubiu: at com.android.server.wm.RootWindowContainer.attachApplication(RootWindowContainer.java:1842)
E biubiubiu: at com.android.server.wm.ActivityTaskManagerService$LocalService.attachApplication(ActivityTaskManagerService.java:6146)
E biubiubiu: at com.android.server.am.ActivityManagerService.attachApplicationLocked(ActivityManagerService.java:5032)
E biubiubiu: at com.android.server.am.ActivityManagerService.attachApplication(ActivityManagerService.java:5122)
E biubiubiu: at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2362)
E biubiubiu: at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2687)
E biubiubiu: at android.os.Binder.execTransactInternal(Binder.java:1280)
E biubiubiu: at android.os.Binder.execTransact(Binder.java:1244)
这个堆栈看着还是很熟悉的,就是进程启动后通知到 system_server 时触发的真正启动 Activity 的调用链.前面的不管,核心调用链如下:
ActivityTaskSupervisor::realStartActivityLocked
Task::minimalResumeActivityLocked
ActivityRecord::completeResumeLocked
ActivityTaskSupervisor::reportResumedActivityLocked
RootWindowContainer::executeAppTransitionForAllDisplay
DisplayContent::executeAppTransition
AppTransition::setReady
先看一下熟悉的 ActivityTaskSupervisor::realStartActivityLocked 方法。
# ActivityTaskSupervisor
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
// 判断是否执行完了pause 。 也就是2个条件之一,必须要执行完pause才可以进入后面
if (!mRootWindowContainer.allPausedActivitiesComplete()) {
// 不满足添加就打log
ProtoLog.v(WM_DEBUG_STATES,
"realStartActivityLocked: Skipping start of r=%s some activities pausing...",
r);
return false;
}
......
// wm_restart_activity
EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),
task.mTaskId, r.shortComponentName);
// 创建执行Activity启动事务
final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);
......
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
// a resume.
// 重点* 设置为 RESUMED
rootTask.minimalResumeActivityLocked(r);
} else {......}
......
}
这个方法是触发 Activity 启动的关键,打印了日志,创建了 LaunchActivityItem 等事务,不过这些都不算当前的重点,当下的重点是看最后的 Task::minimalResumeActivityLocked 方法。
这个方法传递的参数是 TargetActivity 对应的 ActivityRecord ,主要是因为 TargetActivity 即将,所以这里需要将其 ActivityRecord 的状态设置为 RESUMED 。
# Task
void minimalResumeActivityLocked(ActivityRecord r) {
// log
ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (starting new instance) "
+ "callers=%s", r, Debug.getCallers(5));
// 设置状态为RESUMED
r.setState(RESUMED, "minimalResumeActivityLocked");
// Resume 完成的后续处理
r.completeResumeLocked();
......
}
这里做了3个事:
-
- 打印对应的 ProtoLog
-
- 设置状态为RESUMED
-
- 完成的后续处理
而我们现在要看到就是“后续处理”
# ActivityRecord
final ActivityTaskSupervisor mTaskSupervisor;
void completeResumeLocked() {
final boolean wasVisible = mVisibleRequested;
setVisibility(true);
......
// 上报
mTaskSupervisor.reportResumedActivityLocked(this);
......
}
# ActivityTaskSupervisor
boolean reportResumedActivityLocked(ActivityRecord r) {
// A resumed activity cannot be stopping. remove from list
mStoppingActivities.remove(r);
final Task rootTask = r.getRootTask();
if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
// 确保可见性
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
// for all displays in this stage.
// 触发 AppTransitio
mRootWindowContainer.executeAppTransitionForAllDisplay();
return true;
}
return false;
}
这边触发了 RootWindowContainer 的2个方法,第一个确保可见性的之前看过了,当前主要看下面和 AppTransition 相关的流程
# RootWindowContainer
void executeAppTransitionForAllDisplay() {
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
display.mDisplayContent.executeAppTransition();
}
}
这个方法就是遍历每个屏幕,然后执行 DisplayContent::executeAppTransition 方法
# DisplayContent
void executeAppTransition() {
mTransitionController.setReady(this);
// 1. 进入条件
if (mAppTransition.isTransitionSet()) {
// 2. 打印log
ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
"Execute app transition: %s, displayId: %d Callers=%s",
mAppTransition, mDisplayId, Debug.getCallers(5));
// 3. 重点*设置状态为APP_STATE_READY
mAppTransition.setReady();
// 4. 请求刷新
mWmService.mWindowPlacerLocked.requestTraversal();
}
}
这个方法很重要,常说的 executeAppTransition 流程其实指的也就是这个方法:
-
- 进入条件为 AppTransition::isTransitionSet ,这个前面 AppTransition::prepareAppTransition 已经提过
-
- 打印关键日志,同时打印堆栈
-
- 调用 AppTransition::setReady ,将状态设置为 APP_STATE_READY
-
- 当设置为条件满足后,会触发一次 layout,而下一次 AppTransition::isReady 已经为 true,就可以进入后续的逻辑了。
这里先看一下打印的堆栈:
09-27 15:42:47.656 12544 14134 W WindowManager: Execute app transition: mNextAppTransitionRequests=[TRANSIT_OPEN, TRANSIT_OPEN], mNextAppTransitionFlags=, displayId: 0
Callers=com.android.server.wm.RootWindowContainer.executeAppTransitionForAllDisplay:2185
com.android.server.wm.ActivityTaskSupervisor.reportResumedActivityLocked:1887
com.android.server.wm.ActivityRecord.completeResumeLocked:6012
com.android.server.wm.Task.minimalResumeActivityLocked:4831
com.android.server.wm.ActivityTaskSupervisor.realStartActivityLocked:976
这个堆栈和上面的分析也是一致的。
2.2.3 executeAppTransition 流程补充
在上面看到 DisplayContent::executeAppTransition 方法既然会打印堆栈,那就说明会有多个地方会触发。 我第一反应就是 SourceActivity 完成 pause 流程后触发的 realStartActivityLocked 方法逻辑。不过根据这个里调用链值打印了5层,好像也打印不出来是哪个渠道执行到 ActivityTaskSupervisor::realStartActivityLocked 方法的。 于是我同一个操作反复抓取日志后,还真发现了不同的堆栈;
10-25 20:04:39.898 1422 1492 W WindowManager: Execute app transition: mNextAppTransitionRequests=[TRANSIT_OPEN, TRANSIT_OPEN], mNextAppTransitionFlags=, displayId: 0 Callers=com.android.server.wm.ActivityRecord.onStartingWindowDrawn:6474
com.android.server.wm.WindowState.performShowLocked:4711
com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked:283
com.android.server.wm.DisplayContent.lambda$new$8$com-android-server-wm-DisplayContent:995
com.android.server.wm.DisplayContent$$ExternalSyntheticLambda14.accept:4
因为只打印了5级,所以可能还不够明显,下面是完整的堆栈:
10-25 20:04:39.898 1422 1492 E biubiubiu: java.lang.Exception
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.AppTransition.setReady(AppTransition.java:281)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.DisplayContent.executeAppTransition(DisplayContent.java:5366)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.ActivityRecord.onStartingWindowDrawn(ActivityRecord.java:6474)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowState.performShowLocked(WindowState.java:4711)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked(WindowStateAnimator.java:283)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.DisplayContent.lambda$new$8$com-android-server-wm-DisplayContent(DisplayContent.java:995)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.DisplayContent$$ExternalSyntheticLambda14.accept(Unknown Source:4)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2642)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2632)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowState.applyInOrderWithImeWindows(WindowState.java:4977)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowState.forAllWindows(WindowState.java:4821)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1629)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1646)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.DisplayContent.applySurfaceChangesTransaction(DisplayContent.java:4700)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.RootWindowContainer.applySurfaceChangesTransaction(RootWindowContainer.java:1027)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:828)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:788)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:178)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:115)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:57)
10-25 20:04:39.898 1422 1492 E biubiubiu: at android.os.Handler.handleCallback(Handler.java:942)
10-25 20:04:39.898 1422 1492 E biubiubiu: at android.os.Handler.dispatchMessage(Handler.java:99)
10-25 20:04:39.898 1422 1492 E biubiubiu: at android.os.Looper.loopOnce(Looper.java:210)
10-25 20:04:39.898 1422 1492 E biubiubiu: at android.os.Looper.loop(Looper.java:297)
10-25 20:04:39.898 1422 1492 E biubiubiu: at android.os.HandlerThread.run(HandlerThread.java:67)
10-25 20:04:39.898 1422 1492 E biubiubiu: at com.android.server.ServiceThread.run(ServiceThread.java:44)
也是屏幕刷新调用的,忽略掉一些通用逻辑的部分,提取到的调用链如下:
RootWindowContainer::performSurfacePlacementNoTrace
RootWindowContainer::applySurfaceChangesTransaction
DisplayContent::applySurfaceChangesTransaction
DisplayContent::mApplySurfaceChangesTransaction
WindowStateAnimator::commitFinishDrawingLocked
WindowState::performShowLocked
ActivityRecord::onStartingWindowDrawn
DisplayContent::executeAppTransition
AppTransition::setReady
这个流程看着就是 FinishDrawing 流程了,这里应该不是 Activity 的窗口的 FinishDrawing ,所以需要看一下 WindowStateAnimator::commitFinishDrawingLocked 方法极其内部打印
# WindowStateAnimator
boolean commitFinishDrawingLocked() {
......
// mWin的打印是自己加的
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s mWin= %s",
mSurfaceController,mWin);
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
// 后续逻辑
result = mWin.performShowLocked();
}
return result;
}
打印如下:
10-25 20:04:39.898 1422 1492 I WindowManager: commitFinishDrawingLocked: mDrawState=READY_TO_SHOW Surface(name=Splash Screen com.google.android.dialer)/@0x99496f8 mWin= Window{97dc59d u0 Splash Screen com.google.android.dialer}
根据日志发现是 “Splash Screen” 类型的 Window 走到了 FinishDrawing 逻辑最终触发了 DisplayContent::executeAppTransition。
【StartWindow-SplashScreen介绍】里对“Splash Screen”做了详细的解释,这里知道他是属于 StartWindow 就好,然后是在应用 Window 显示前,为了更好过渡效果出现的一个 Window 。 这个 StartWindow 的 type 就是 TYPE_APPLICATION_STARTING 所以会进入 WindowState::performShowLocked 方法,那么也就是说只有 StratWindow 的流程走到将状态设置为 READY_TO_SHOW时,才会满足条件,进入到执行 DisplayContent::executeAppTransition 逻辑进而执行到 AppTransition::setReady 。
2.2.4 executeAppTransition 流程小结
这个流程的目的就是执行 AppTransition::setReady ,将 AppTransition 的状态设置为 APP_STATE_READY 。后续就可以执行对应的 goodToGo 流程了。 根据前面的分析发现执行过来的调用链会有多个,首先我们知道各个场景都有会 Activity 切换事件,所以 AppTransition 内部定义了多大13种 TransitionType 。既然有这么多场景会触发 AppTransition 那自然也会有多个地方触发 executeAppTransition 流程 ,所以打印堆栈也是应该的。
而我直接没理解到这点,以为是当前分析的冷启动也有多个调用链,本来是以为 activityPaused 流程也会触发 DisplayContent::executeAppTransition 逻辑的(实际上应该也是会的,单本质上也是 ActivityTaskSupervisor::realStartActivityLocked 后的5层堆栈)结果误打误撞发现 StartWindow 的 FinishDrawing 流程也会触发。
其他场景目前就不管了,从当前冷启动场景执行到知道有以下2个场景会触发:
-
- ActivityTaskSupervisor::realStartActivityLocked 后的调用,说明 Activity 马上启动,应用窗口要显示了
-
- StartWindow 绘制完成
我个人总结如下:
-
- 个人经过多次抓取,大部分情况都是 realStartActivityLocked 场景进入的 DisplayContent::executeAppTransition 逻辑
-
- 两种逻辑的先后顺序,本质上的区别还是看目标应用进程的创建快慢。如果进程创建的快,那么就的第一种,否则就是 StartWindow 流程先执行,也就是第二种
-
- 不管是 StartWindow 显示 真正的 Activity要显示。本质上都是说这个 ActivityRecord 下面有(或马上有)窗口了,那就可以执行 AppTransition。 (真正做动画的时候有窗口内容就行)