因为项目涉及一些启动相关的方案,以及平常处理问题的时候会遇到很多启动响应的问题,所以对启动动效的拉起流程进行一个全面的梳理,同时也借此即会对U版本的动效流程做一个初步的了解吧。
startActivityUnchecked:
启动的流程就先不展开细说了,直接跳到开启启动动效处理的地方。这里直接来到ActivityStarter类的startActivityunchecked方法中:
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment, boolean restrictedBgActivity,
NeededUriGrants intentGrants) {
int result = START_CANCELED;
final Task startedActivityRootTask;
final TransitionController transitionController = r.mTransitionController;
Transition newTransition = (!transitionController.isCollecting()
&& transitionController.getTransitionPlayer() != null)
? transitionController.createTransition(TRANSIT_OPEN) : null; //(1)
RemoteTransition remoteTransition = r.takeRemoteTransition();//(2)
try {
mService.deferWindowLayout();
transitionController.collect(r);//(3)
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
intentGrants); //(4)
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
startedActivityRootTask = handleStartResult(r, options, result, newTransition,
remoteTransition); //(5)
}
} finally {
mService.continueWindowLayout();
}
postStartActivityProcessing(r, result, startedActivityRootTask);
return result;
}
在代码(1)处,创建了一个动效的Transition类的对象,这个Transition类表示的是视图树中,从一种状态过渡到另一种状态的过渡动画。也就是说在启动应用的时候需要从桌面打开应用,在这个过程中,应用从桌面图标放大到全屏的一个动画过程就可以理解为一个Transition。
这里首先判断当前是否存在正在收集过程中的Transition对象,然后就是当前是否有能够处理Transition的播放器。然后新建一个处于TRANSIT_OPEN状态的Transtion。
1、 createTransition:
然后就来到创建Transition对象的地方,看看创建一个Transition对象需要做哪些准备。
private Transition createTransition(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) { //这里就是前面的getTransitionPlayer的判断
throw new IllegalStateException("Shell Transitions not enabled");
}
if (mCollectingTransition != null) { //其实这里和isCollecting的判断效果是一样的
throw new IllegalStateException("Simultaneous transition collection not supported"
+ " yet. Use {@link #createPendingTransition} for explicit queueing.");
}
Transition transit = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine); //(6)创建新的对象
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", transit);
moveToCollecting(transit); //(7)
return transit;
}
Transition(@TransitionType int type, @TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
mType = type;
mFlags = flags;
mController = controller;
mSyncEngine = syncEngine;
mToken = new Token(this);
controller.mTransitionTracer.logState(this);
}
如上代码,其实这里没有太多的处理,就是将前面的条件又重新确认了一遍,然后就开始创建新的Transition对象。这里创建Transition的过程,也很简单,就是将传入的参数赋值给Transition对象的属性。
void moveToCollecting(@NonNull Transition transition) {
if (mCollectingTransition != null) {
throw new IllegalStateException("Simultaneous transition collection not supported.");
}
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();
}
void startCollecting(long timeoutMs, int method) {
if (mState != STATE_PENDING) {
throw new IllegalStateException("Attempting to re-use a transition");
}
mState = STATE_COLLECTING;
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG, method);
mController.mTransitionTracer.logState(this);
}
int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
int method) {
final SyncGroup s = prepareSyncSet(listener, name, method);
startSyncSet(s, timeoutMs); //(8)
return s.mSyncId;
}
void startSyncSet(SyncGroup s, long timeoutMs) {
if (mActiveSyncs.size() != 0) {
ProtoLog.w(WM_DEBUG_SYNC_ENGINE,
"SyncGroup %d: Started when there is other active SyncGroup", s.mSyncId);
}
mActiveSyncs.put(s.mSyncId, s);
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s",
s.mSyncId, s.mListener);
scheduleTimeout(s, timeoutMs);
}
然后就执行到代码(7)处,moveToCollecting()。该方法首先将刚刚新建的transition赋值给TransitionController对象的mCollectingTransition,然后初始化一个timeOutMs。然后就通过这两个属性开始了收集的流程。再进入到startCollecting方法。这个方法就是将transition的mState修改为STATE_COLLECTING(初始状态为STATE_PENDING)。然后再创建一个SyncGroup对象去收集动效相关的WindowContainer,然后又调用了同名的函数startSyncSet方法,这里又将新建的SyncGroup对象放入了mActiveSyncs中,标志着当前是有transition处于激活状态的。
处理完这些,返回刚刚新建的SyncGroup对象的mSyncId,并赋值给新建的transition。于是到这里,第一个功能createTransition执行完毕。
2、 takeRemoteTransition:
takeRemoteTransition方法是ActivityRecord中的方法。这个方法的功能就是返回mPendingRemoteTransition,并再将其置空,我推测,这个mPendingRemoteTransition就像一个消耗品,只能用一次,所以会在用完再将其置空。
RemoteTransition takeRemoteTransition() {
RemoteTransition out = mPendingRemoteTransition;
mPendingRemoteTransition = null;
return out;
}
private void setOptions(@NonNull ActivityOptions options) {
mLaunchedFromBubble = options.getLaunchedFromBubble();
mPendingOptions = options;
if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) {
mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
}
mPendingRemoteTransition = options.getRemoteTransition();
}
void updateOptionsLocked(ActivityOptions options) {
if (options != null) {
if (DEBUG_TRANSITION) Slog.i(TAG, "Update options for " + this);
if (mPendingOptions != null) {
mPendingOptions.abort();
}
setOptions(options);
}
}
mPendingRemoteTransition的赋值是在setOptions方法中。因为其是私有方法,所以只有在类中调用,但是updateOptionsLocked方法就是一个public方法,这个调用的地方就多了。目前这边的具体调用我还没整理清楚,只知道是从桌面或者SystemUI发送过来的。看其定义就能知道,mPendingRemoteTransition的类型是RemoteTransition,而该类实现了Parcelable,所以必定会跨进程传输的。
3、 collect
在获取到remoteTransition之后,就开始进入收集状态了,这时候需要通过transitionController调用到Transition类的collect方法去收集参与动效的一些窗口了。具体流程进入到代码(3)处。
void collect(@NonNull WindowContainer wc) {
if (mState < STATE_COLLECTING) {
throw new IllegalStateException("Transition hasn't started collecting.");
}
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);
// "snapshot" all parents (as potential promotion targets). Do this before checking
// if this is already a participant in case it has since been re-parented.
for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
curr = curr.getParent()) { //(9)
mChanges.put(curr, new ChangeInfo(curr));
if (isReadyGroup(curr)) {//(10)
mReadyTracker.addGroup(curr);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
+ " Transition %d with root=%s", mSyncId, curr);
}
}
if (mParticipants.contains(wc)) return;//(11) 若当前wc已经在参与者中,就直接返回了。
// Wallpaper is like in a static drawn state unless display may have changes, so exclude
// the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
final boolean needSyncDraw = !isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent); //(12)
if (needSyncDraw) {
mSyncEngine.addToSyncSet(mSyncId, wc); //(13)
}
ChangeInfo info = mChanges.get(wc);
if (info == null) {
info = new ChangeInfo(wc);
mChanges.put(wc, info); //(14)
}
mParticipants.add(wc); //(15)
if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
mTargetDisplays.add(wc.getDisplayContent()); //(16)
}
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
final WindowState wallpaper =
wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
if (wallpaper != null) {
collect(wallpaper.mToken); //(17)
}
}
}
根据上述代码可见,这里的流程首先还是确认下mState的状态,因为在前面给mState已经置为STATE_COLLECTING了,所以这两项异常检查直接跳过。然后来到for循环处(9),在这个循环中,从传入处的wc开始向上遍历其父节点,直到其父节点为空(到顶了)或者mChanges中包含其父节点为止。而for循环中的操作,首先就是将curr放进mChange中。mChange就是一个Map结构,key为WindowContainer,value为ChangeInfo。
代码(10)处的判断就是看curr是不是DisplayContent类的对象,这里可以参见之前的一篇博客,看一下这个窗口的结构:WindowManager相关容器类_taskfrag-CSDN博客。然后再将curr放进ReadyTracker类的成员mReadyGroups中,这个mReadyGroup也是一个MAP,他的key是WindowContainer,value则是一个Boolean类对象。
然后看到(12)处,这里就是将wc放入之前创建的SyncGroup对象中的mRootMembers中,然后再执行prepaerSync(),将当前的wc的所有父节点的mSyncState置为SYNC_STATE_READY,这里为什么父节点直接置为ready,主要是父节点其实只要等子节点置为ready,其就可以置为ready,所以最终还会调用到WindowState类的prepareSync方法。而这一部分其实在这篇博客也详细解释过:WCT系列(四):BLASTSyncEngine-CSDN博客。只有等待对应的窗口触发finishDrawing才可以将WindowState对象的状态置为SYNC_STATE_READY。
到了这里,collect流程也结束了。
4、 handleStartResult:
再次回到startActivityunchecked方法中,因为本次主要梳理动效流程,所以直接看到handleStartResult方法。这个方法也很长,最终重点其实只落在了requestStartTransition这个方法上。
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);
ActivityManager.RunningTaskInfo info = null;
if (startTask != null) {
info = new ActivityManager.RunningTaskInfo();
startTask.fillTaskInfo(info);
}
mTransitionPlayer.requestStartTransition(transition.getToken(),
new TransitionRequestInfo(transition.mType, info, remoteTransition,
displayChange));
transition.setRemoteTransition(remoteTransition);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting transition", e);
transition.start();
}
return transition;
}
requestStartTransition方法其实就是为了发送一个binder给SystemUI进程,请求开启一个StartTransition,这一步如果平时看trace的话,其实就会很熟悉,在SystemServer进程的startActivityInner这个TAG点下就会有一个异步binder,发往SystemUI进程,请求开启一个startTransition。然后后面SystemServer进程中的onSurfacePlacement这个TAG点下还会发送一个onTransitionReady的binder发往SystemUI,这里就是我们所有的wc的mState都置为SYNC_STATE_READY时会触发的下一个流程了。这里暂时留到下一篇博客讲解。在发送onTransitionReady之后,SystemUI进程就会开启一系列的处理逻辑,最后通过onAnimationStart这个异步binder再通知到桌面开启启动动效。