忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
-- 服装学院的IT男
ShellTransitions-2-requestStartTransition处理
分析代码前,先回顾一下 Activity 启动流程走到 system_server 进程后的调用链:
ActivityTaskManagerService::startActivity
ActivityTaskManagerService::startActivityAsUser
ActivityTaskManagerService::startActivityAsUser
ActivityStartController::obtainStarter
ActivityStarter::execute
ActivityStarter::executeRequest -- 构建 ActivityRecord -- 创建ActivityRecord
ActivityStarter::startActivityUnchecked
ActivityStarter::startActivityInner -- 关键函数startActivityInner
ActivityStarter::getOrCreateRootTask -- 创建或者拿到Task
ActivityStarter::setNewTask -- 将task与activityRecord 绑定
RootWindowContainer::resumeFocusedTasksTopActivities -- 处理需要显示的Activity
本次的分析从 ActivityStarter::startActivityUnchecked 方法开始。
整体流程如下:
# ActivityStarter
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment, @BalCode int balCode,
NeededUriGrants intentGrants, int realCallingUid) {
int result = START_CANCELED;
......
// 获取 Transition 的管理类
final TransitionController transitionController = r.mTransitionController;
// 1. 创建一个 Transition
Transition newTransition = transitionController.isShellTransitionsEnabled()
? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
// 获取一个远程过渡动画的参数
RemoteTransition remoteTransition = r.takeRemoteTransition();
try {
// 延迟layout
mService.deferWindowLayout();
// 2.1 收集 ActivityRecord
transitionController.collect(r);
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
// 2.2 启动主流程 (收集 Task)
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, options, inTask, inTaskFragment, balCode,
intentGrants, realCallingUid);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
// 3. 启动后处理
startedActivityRootTask = handleStartResult(r, options, result, newTransition,
remoteTransition);
}
} finally {
// 恢复layout
mService.continueWindowLayout();
}
postStartActivityProcessing(r, result, startedActivityRootTask);
return result;
}
这里获取的 transitionController 其实是 WMS 的一个成员变量,全局也只有1个对象,并不存在多个,用于控制 Transition 逻辑。
这段代码有以下3个点需要注意:
-
- 为当前操作创建一个 Transition ,这里的逻辑也会触发 SyncGroup 的创建,也就是 BLASTSyncEngine 的第一步
-
- 收集容器
- 2.1 收集 ActivityRecord 到同步组
- 2.2 收集 Task 到同步组
-
- 触发 requestStartTransition 流程
1. 创建过渡事务和同步组
TransitionController::createAndStartCollecting 会创建一个过渡事务并且收集,看参数传递是的 “TRANSIT_OPEN”
# TransitionController
BLASTSyncEngine mSyncEngine;
Transition createAndStartCollecting(int type) {
......
// 同步引擎中已经在处理同步事务
if (mSyncEngine.hasActiveSync()) {
// 如果正在收集的处理
......
return null;
}
// 新建个事务
Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine);
// 开始收集
moveToCollecting(transit);
return transit;
}
这里有2种情况:
-
- 同步引擎中已经有任务在处理,这个场景当前暂时不分析
-
- 同步引擎中没有任务在处理,则创建出一个 Transition ,然后执行 moveToCollecting
1.1 创建Transition
# Transition
// 过渡事务类型
final @TransitionType int mType;
// 唯一标识
private final Token mToken;
Transition(@TransitionType int type, @TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
mType = type;
......
// 内部类,binder 类型
mToken = new Token(this);
......
}
mType : 和之前的 AppTransition 一样,表示当前过渡的类型,几个场景的类型在 【应用启动动画-app_transition-2】有介绍 Token : 内部类,是个binder, 也就是当前 Transition 的唯一标识。看来是要跨进程使用
1.2 创建同步组
这个方法的目的是要把参数的 Transition 作为这次过渡事务收集的主事务,并且创建一个同步组。
如果执行这个方法的时候,已经有主事务了,则会报错 。
# TransitionController
// 当前系统中正在收集过渡的Transition
private Transition mCollectingTransition = null;
private ITransitionPlayer mTransitionPlayer;
// 默认超时时间,0.5秒, 和同步事务一样
private static final int DEFAULT_TIMEOUT_MS = 5000;
void moveToCollecting(@NonNull Transition transition) {
// 如果已经有正在收集的主事务了,则报错
if (mCollectingTransition != null) {
throw new IllegalStateException("Simultaneous transition collection not supported.");
}
// 异常处理,SystemUI 被杀了才会为null
if (mTransitionPlayer == null) {
// If sysui has been killed (by a test) or crashed, we can temporarily have no player
// In this case, abort the transition.
transition.abort();
return;
}
// * 赋值给主事务
mCollectingTransition = transition;
// Distinguish change type because the response time is usually expected to be not too long.
final long timeoutMs =
transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
// * 开始收集
mCollectingTransition.startCollecting(timeoutMs);
// 打印日志
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
mCollectingTransition);
dispatchLegacyAppTransitionPending();
}
打印日志: WindowManager: Start collecting in Transition: TransitionRecord{6e037e id=40 type=OPEN flags=0x0}
这里的主流程是 Transition::startCollecting 方法,开始收集过渡事务。
# Transition
// 实现接口
Transition implements BLASTSyncEngine.TransactionReadyListener{
// 当前事务的状态
private @TransitionState int mState = STATE_PENDING;
void startCollecting(long timeoutMs) {
// 如果不是默认状态,则说明之前已经开始过收集,再执行这个方法就是bug了
if (mState != STATE_PENDING) {
throw new IllegalStateException("Attempting to re-use a transition");
}
// 设置状态,表示开始收集
mState = STATE_COLLECTING;
// 开始一个同步组
// 注意第一个参数。当前实现了 TransactionReadyListener 所以会执行 onTransactionReady
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG,
mParallelCollectType != PARALLEL_TYPE_NONE);
mSyncEngine.setSyncMethod(mSyncId, TransitionController.SYNC_METHOD);
......
}
}
-
- 设置过渡事务的状态为 STATE_COLLECTING
-
- 通过同步引擎开始一个同步组,内部逻辑不需要再解释了,这边留意穿进去的 TransactionReadyListener 是 “this” ,也就是说等同步完成后,会执行 Transition 下的 onTransactionReady 回调方法。
1.3 小结
-
- 创建了事务,并且设置为主事务
-
- 创建了同步组,传递的回调为 Transition
2. 收集容器
这里会收集2个容器,新启动应用的 ActivityRecord 和 Task 。
2.1 收集 ActivityRecord
这一步的触发在 ActivityStarter::startActivityUnchecked 方法中 “transitionController.collect(r);” 的调用,参数 r 为启动 Activity 对应的 ActivityRecord 。
# TransitionController
private Transition mCollectingTransition = null;
void collect(@NonNull WindowContainer wc) {
if (mCollectingTransition == null) return;
mCollectingTransition.collect(wc);
}
调用的是这次的主事务 Transition 对应的收集。
# Transition
private final BLASTSyncEngine mSyncEngine;
// 已经收集过的容器
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
// 保存这次事务中,参与者(容器)的哪些属性发生了改变
final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>();
void collect(@NonNull WindowContainer wc) {
// 没有执行 Transition::startCollecting 则报错
if (mState < STATE_COLLECTING) {
throw new IllegalStateException("Transition hasn't started collecting.");
}
// 不处于收集状态则不处理 状态为 STATE_COLLECTING 或者 STATE_STARTED 都满足收集条件
if (!isCollecting()) {
// Too late, transition already started playing, so don't collect.
return;
}
// 日志,收集了某个容器
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
mSyncId, wc);
// 想要收集对应的屏幕到 ReadyTracker 中
// getAnimatableParent方法会一直获取这个容器的父容器,直到是 DisplayContent
for (WindowContainer<?> curr = getAnimatableParent(wc);
curr != null && !mChanges.containsKey(curr);
curr = getAnimatableParent(curr)) {
final ChangeInfo info = new ChangeInfo(curr);
updateTransientFlags(info);
mChanges.put(curr, info);
// 如果是 DisplayContent
if (isReadyGroup(curr)) {
// 放入跟踪集合
mReadyTracker.addGroup(curr);
// 打印日志
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
+ " Transition %d with root=%s", mSyncId, curr);
}
}
......
// 非壁纸窗口这里 needSync 一般都为 true
// 判断条件如下:
// 不是壁纸或者容器所在的这个屏幕被收集
// 并且容器没有被暂时隐藏
final boolean needSync = (!isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent))
// Transient-hide may be hidden later, so no need to request redraw.
&& !isInTransientHide(wc);
if (needSync) {
// * 1. 加入到同步组
mSyncEngine.addToSyncSet(mSyncId, wc);
}
// * 2. 获取容器对应的 ChangeInfo ,记录这个容器哪些属性发生了改变
ChangeInfo info = mChanges.get(wc);
if (info == null) {
// 如果之前没有保存则新建
info = new ChangeInfo(wc);
updateTransientFlags(info);
// 保存到集合
mChanges.put(wc, info);
}
// * 3. 保存到集合
mParticipants.add(wc);
......
}
-
- 通过 BLASTSyncEngine 添加容器到同步组
-
- 创建对应的 ChangeInfo 并保存
-
- 添加进 mParticipants 集合
打印日志: WindowManager: Collecting in transition 40: ActivityRecord{55c10df u0 com.android.dialer/.main.impl.MainActivity
这里还有个点,在 “mReadyTracker.addGroup(curr); ”这部分。 目前分析进来的场景是收集 ActivityRecord 不过由于 ActivityRecord 还没挂载到层级树,所以这部分代码现在相当于无效逻辑,当前有个印象即可,后面会详细解释。
2.2 收集 Task
在 ActivityStarter::startActivityUnchecked 方法中 “result = startActivityInner(r,..);” 的调用,这里方法内部会创建 Task ,同时也会将其收集到同步组。
# ActivityStarter
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord...) {
......
if (mTargetRootTask == null) {
// 创建Task
mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
mOptions);
}
if (newTask) {
// taskToAffiliate 为null
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
// * 将需要启动的ActivityRecord与 新创建的Task 进行绑定
setNewTask(taskToAffiliate);
} ......
......
}
setNewTask 方法会将新建的 Task 和 ActivityRecord 绑定,当前要分析的流程也在这。
# ActivityStarer
private void setNewTask(Task taskToAffiliate) {
// 为true
final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
// 就是mTargetRootTask,也就是刚刚创建的Task
final Task task = mTargetRootTask.reuseOrCreateTask(
mStartActivity.info, mIntent, mVoiceSession,
mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
// * 收集 Task 容器过渡事务
task.mTransitionController.collectExistenceChange(task);
// ActivityRecord的挂载
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");
......
}
后续的逻辑来到 TransitionController::collectExistenceChange 方法。 这个方法表示收集的是“存在性”变化,也就是“从无到有,或者从有到无”。当前这个 Task 是冷启动新建的,所以是“从无到有”。
# TransitionController
void collectExistenceChange(@NonNull WindowContainer wc) {
if (mCollectingTransition == null) return;
mCollectingTransition.collectExistenceChange(wc);
}
这里的 mCollectingTransition 就是当前的主事务。
# Transition
final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>();
void collectExistenceChange(@NonNull WindowContainer wc) {
// 如果已经开始播放动画了,则不能再收集了
if (mState >= STATE_PLAYING) {
// Too late to collect. Don't check too-early here since `collect` will check that.
return;
}
// 日志
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Existence Changed in transition %d:"
+ " %s", mSyncId, wc);
// 1. 收集这个容器,当前是 Task
collect(wc);
// 2. 存在性改变
mChanges.get(wc).mExistenceChanged = true;
}
Transition::collect 的调用和前面收集 ActivityRecord 到同步组是一样的,只不过这里的参数是 Task 。 另外还把这个容器对应的 ChangeInfo 的 mExistenceChanged 变量设置为 true 标记了事务的改变类型。
打印日志: WindowManager: Existence Changed in transition 11: Task{9910f99 #16 type=standard A=10162:com.android.dialer}
3. 触发 requestStartTransition 流程
这个逻辑的调用在 ActivityStarter::startActivityUnchecked 方法调用 ActivityStarter::handleStartResult 。
# ActivityStarter
private @Nullable Task handleStartResult(@NonNull ActivityRecord started,
ActivityOptions options, int result, Transition newTransition,
RemoteTransition remoteTransition) {
......
if (isStarted) {
// The activity is started new rather than just brought forward, so record it as an
// existence change.
// 记录 ActivityRecord 从无到有
transitionController.collectExistenceChange(started);
} ......
......
if (newTransition != null) {
// * 请求 SystemUI 开始事务
transitionController.requestStartTransition(newTransition,
mTargetTask == null ? started.getTask() : mTargetTask,
remoteTransition, null /* displayChange */);
}
......
}
-
- 参数 newTransition 是在 ActivityStarter::startActivityUnchecked 方法新建的过渡事务
-
- 这里会和 Task 一样记录 ActivityRecord 的“存在性”变化。区别是由于之前 ActivityRecord 已经添加进同步组了,所以这次不会再次添加
-
- 后续逻辑会触发第一次 WMCore-> WMShell 调用
对应新启动应用 Task 和 ActivityRecord 收集和存在性记录的顺序为:
- 收集的顺序:ActivityRecord -> Task
- 存在性记录顺序:Task -> ActivityRecord
收集容器到同步组顺序日志:
WindowManager: SyncGroup 6: Adding to group: ActivityRecord{8a1f45f u0 com.android.dialer/.main.impl.MainActivity t-1}
WindowManager: SyncGroup 6: Adding to group: Task{5e42eac #13 type=standard A=10162:com.android.dialer}
WindowManager: SyncGroup 6: Adding to group: ActivityRecord{14a87b7 u0 com.android.launcher3/.uioverrides.QuickstepLauncher t12}
存在性变化顺序日志:
WindowManager: Existence Changed in transition 6: Task{5e42eac #13 type=standard A=10162:com.android.dialer}
WindowManager: Existence Changed in transition 6: ActivityRecord{8a1f45f u0 com.android.dialer/.main.impl.MainActivity t13}
继续看主线。
# TransitionController
// 具体执行在 SystemUI 端
private ITransitionPlayer mTransitionPlayer;
Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange) {
......
try {
// 日志
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
// RunningTaskInfo 对象会报错一个正在运行的 Task 的相关信息
ActivityManager.RunningTaskInfo info = null;
if (startTask != null) {
info = new ActivityManager.RunningTaskInfo();
// 填充 info
startTask.fillTaskInfo(info);
}
// 创建一个请求的对象
final TransitionRequestInfo request = new TransitionRequestInfo(
transition.mType, info, remoteTransition, displayChange);
......
// * WMCore ---》WMShell
mTransitionPlayer.requestStartTransition(transition.getToken(), request);
if (remoteTransition != null) {
transition.setRemoteAnimationApp(remoteTransition.getAppThread());
}
} catch (RemoteException e) {
......
}
return transition;
}
构建了一些相关对象,然厚触发了第一次 WMCore-> WMShell 调用。 request 对象包含了很多数据,不过稍后会在 SystemUI 端打印出来,所以可以不用可以去记。
打印日志: WindowManager: Requesting StartTransition: TransitionRecord{6e037e id=40 type=OPEN flags=0x0}
这个 mTransitionPlayer 是个binder 对象,服务端在 SystemUI 。构建与调用链如下:
Transitions::init
TransitionPlayerImpl::init
WindowOrganizer::requestStartTransition -- 跨进程通信,像 WMCore 注册
WindowOrganizerController::requestStartTransition