忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
-- 服装学院的IT男
本篇为应用启动动画 app_transition 的最后一篇,上一篇的流程已经到了 system_server 开始跨进程调用 launcher ,现在就要真的在 launcher 执行执行动画了,这是本篇需要看的流程完整的调用链:
IRemoteAnimationRunner.Stub::onAnimationStart
LauncherAnimationRunner::onAnimationStart
LauncherAnimationRunner.AnimationResult::init --结束回调
QuickstepTransitionManager.AppLaunchAnimationRunner::onAnimationStart
QuickstepTransitionManager.AppLaunchAnimationRunner::composeIconLaunchAnimator
QuickstepTransitionManager.AppLaunchAnimationRunner::getOpeningWindowAnimators
MultiValueUpdateListener::onUpdate
SurfaceParams.Builder::build -- 构建动画参数SurfaceParams
SurfaceTransactionApplier::scheduleApply -- 应用参数到leash
ViewRootImpl::mergeWithNextTransaction -- Transaction的apply
LauncherAnimationRunner.AnimationResult::setAnimation
AnimatorListenerAdapter::onAnimationEnd
LauncherAnimationRunner.AnimationResult::finish -- 触发结束回调执行
1. launcher开始动画
先回忆下上面末尾 RemoteAnimationController::goodToGo 方法里最后执行的一句代码
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
再次看到了 RemoteAnimationController 下的 mRemoteAnimationAdapter 应该非常清楚,就是【动画-app_transition-1】末尾提到的,就是 launcher 传递过来的 RemoteAnimationAdapter。
这里需要回顾一下之前 launcher 里几个关键类的关系图:
RemoteAnimationAdapter 的 getRunner() 返回的就是 RemoteAnimationAdapterCompat 下 wrapRemoteAnimationRunner 返回的匿名 IRemoteAnimationRunner.Stub 对象。 现在重新在看看这个方法。
# RemoteAnimationAdapterCompat
// 参数remoteAnimationAdapter是LauncherAnimationRunne对象
public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(@TransitionOldType int transit,
RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
final RemoteAnimationTargetCompat[] nonAppsCompat =
RemoteAnimationTargetCompat.wrap(nonApps);
// 定义个动画结束的回调,也就是执行参数finishedCallback的onAnimationFinished回调
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
}
}
};
// * 实际上是调用 LauncherAnimationRunner 的 onAnimationStart
remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
nonAppsCompat, animationFinishedCallback);
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
// 实际上是调用LauncherAnimationRunner的onAnimationCancelled
remoteAnimationAdapter.onAnimationCancelled();
}
};
}
根据在 launcher 部分的介绍,直接看 LauncherAnimationRunner
# LauncherAnimationRunner
@BinderThread
public void onAnimationStart(
int transit,
RemoteAnimationTargetCompat[] appTargets, // app目标 2个(桌面和电话)
RemoteAnimationTargetCompat[] wallpaperTargets, 壁纸目标 1个
RemoteAnimationTargetCompat[] nonAppTargets, 非应用目标 0个
Runnable runnable) { // 根据调用过来的地方知道是动画结束的回调
// 构建一个Runnable
Runnable r = () -> {
// 要开始新的动画,所以先执行动画清理工作
finishExistingAnimation();
// 将动画结束的回调包装在AnimationResult中
mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
// 通过工厂模式构建对象并执行 onCreateAnimation
getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
mAnimationResult);
};
// post到下次刷新执行Runnable
if (mStartAtFrontOfQueue) {
postAtFrontOfQueueAsynchronously(mHandler, r);
} else {
postAsyncCallback(mHandler, r);
}
}
这个方法比较重要,都加上了注释,这里 mStartAtFrontOfQueue 是在构建 LauncherAnimationRunner 的时候赋值的, 根据前面的分析传递的是 true 。其实不管 true 还是 fail 都会将构建的Runnable 对象 post 到下一次刷新的时候执行,为 true 的话就表示插队到前面。这个可以不用过于在意,知道是在下一帧的时候会执行 Runnable 即可。
主要看的还是看“getFactory().onCreateAnimation”方法,另外关注新创建的 AnimationResult 什么时候用就表示什么时候动画结束。
"getFactory()"之前也分析过所以直接看 AppLaunchAnimationRunner::onCreateAnimation 即可
# QuickstepTransitionManager.AppLaunchAnimationRunner
@Override
public void onCreateAnimation(int transit,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
// 创建个动画集合
AnimatorSet anim = new AnimatorSet();
...... // 忽略从小部件或者recent打开的分支
// 当前分支
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
......
// 监听动画结束回调,执行 system_server 动画结束的回调
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
skipFirstFrame);
}
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTargetCompat[] appTargets,
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
@NonNull RemoteAnimationTargetCompat[] nonAppTargets,
boolean launcherClosing) {
......
// 获取动画
Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
rotationChange);
windowAnimator.setStartDelay(startDelay);
// 播放动画
anim.play(windowAnimator);
......
}
这里看到了动画的play,那么关键是在 getOpeningWindowAnimators 方法里。
1.1 动画的创建
# QuickstepTransitionManager.AppLaunchAnimationRunner
private Animator getOpeningWindowAnimators(View v,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
RemoteAnimationTargetCompat[] nonAppTargets,
Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
RectF launcherIconBounds = new RectF();
// 拿到launcher内定义的一个图片View,用于launcher自己的动画
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
!appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
......
// 后续触发leash动画更新的对象 ,这里的floatingView的桌面应用图标的动画View
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
......
AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION);
appAnimator.setInterpolator(LINEAR);
appAnimator.addListener(floatingView);// launcher自己内部的动画
appAnimator.addListener(new AnimatorListenerAdapter() {
......
});
// 再定义一个动画的监听,这里会触发对leash的图层做动画
// 这段是很长,但是也是重点,动画图层的update就在这
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
......
// 重点*
@Override
public void onUpdate(float percent, boolean initOnly) {
......
// 定义需要对leash做动画的参数
ArrayList<SurfaceParams> params = new ArrayList<>();
// 遍历appTargets,里面有task要做动画的leash
for (int i = appTargets.length - 1; i >= 0; i--) {
// 重点* 根据leash构造动画参数
SurfaceParams.Builder builder = new SurfaceParams.Builder(.leash);
if (target.mode == MODE_OPENING) {
// 设置缩放比例
matrix.setScale(scale, scale);
// 旋转角度
if (rotationChange == 1) {
......
} else if (rotationChange == 2) {
......
} else if (rotationChange == 3) {
......
} else {
// 正常为0,走这
matrix.postTranslate(windowTransX0, windowTransY0);
}
......
// 设置动画参数
builder.withMatrix(matrix)
.withWindowCrop(crop)
.withAlpha(1f - mIconAlpha.value)
.withCornerRadius(mWindowRadius.value)
.withShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_CLOSING) {
......// 关闭的处理,类似上面,根据之前的分析,这个是对launcher的动画
}
// 执行build构建出SurfaceParams然后添加到集合
params.add(builder.build());
}
......
// 开始应用动画
surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
}
}
// 将上面构造的监听添加进去
appAnimator.addUpdateListener(listener);
......
return animatorSet;
}
这段代码缩略后还是很长,但是主要做的事情比较简单,首先是创建了动画对象,其次是在 MultiValueUpdateListener::onUpdate 监听动画的更新。动画最重要的就是 onUpdate 了,可以看到这边是构造使用leash 图层构造了一个 SurfaceParams 对象,然后对 Matrix 透明度等一些属性的设置也都在这个对象里,然后再通过 SurfaceTransactionApplier::scheduleApply 方法来应用属性,达到动画目的。 那么对于 leash 图层也保存在 SurfaceParams 对象里了,当前动画时间段需要显示的属性也在 SurfaceParams 对象了,剩下的看怎么将去构建一个 Transaction ,然后把 leash 和参数都应用起来就可以了。 先看一下 SurfaceParams 的 Builder
# SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder
final SurfaceControl surface;
public Builder(SurfaceControl surface) {
this.surface = surface;
}
这里构建Builder的时候就会将leash保存在 surface 变量中,后面执行 build() 方法的时候就作为了 SurfaceParams 下的 surface 变量。
然后继续看下面流程:
# SurfaceTransactionApplier
private final ViewRootImpl mTargetViewRootImpl;
public void scheduleApply(final SurfaceParams... params) {
// 拿到图标动画的View
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
// 构建一个Transaction最终执行动画图层的应用
Transaction t = new Transaction();
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
if (surfaceParams.surface.isValid()) {
// 将前面构建的参数应用到Transaction中
surfaceParams.applyTo(t);
}
}
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
setCanRelease(false);
// 注册回调,下一帧的时候执行动画的修改
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
return;
}
// 应用事务,真正让属性应用到leash图层中
mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
});
// Make sure a frame gets scheduled.
view.invalidate();
}
那真正要对一个 Surace 做改变,我们知道肯定是要通过 SurfaceControl.Transaction 来完成的。 所以这个方法就是创建一个 SurfaceControl.Transaction 对象,然后把之前构造的SurfaceParams 的参数应用到这个 Transaction 中,那么现在动画需要改变的属性都在这个 Transaction 里,只要有最后进行 Transaction::apply 就完成了。 这个方法的后面部分就对下一帧刷新做了监听,也就是说下一帧来的时候就会执行 ViewRootImpl::mergeWithNextTransaction ,那么内部肯定是会执行 Transaction::apply 的。
# ViewRootImpl
public void mergeWithNextTransaction(Transaction t, long frameNumber) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
} else {
// 执行apply
t.apply();
}
}
到此,app_transition 的动画是怎么执行生效的就全部完成了,这里的逻辑比较简单,和普通写应用的动画差不多,在 onUpdate 里根据动画的时间,计算出当前需要显示的透明度,位置和大小,圆角等属性,然后应用到leash图层,以达到动画的效果。
剩下的就是看看动画结束的回调,是怎么传递到 system_server 进程了。
1.2 FloatingView
动画更新相关的代码很多地方提到了 FloatingView,这里只看一下FloatingView的构造方法知道他是做什么的。
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
@TargetApi(Build.VERSION_CODES.Q)
public class FloatingIconView extends FrameLayout implements
Animator.AnimatorListener, OnGlobalLayoutListener, FloatingView {
......
}
看上面的注释就是图标放大的动画,然后看来是Q版本才有这个动效。关于图标的动画是View动画,不在分析的范围内,后面有时间单独分析。
2. 动画结束回调
根据之前的代码,现在有2个动画结束回调的:
- WindowContainer 构造 SurfaceAnimator 时传递的 WindowContainer::onAnimationFinished
- RemoteAnimationController::goodToGo 开始触发 launcher 动画传递来的结束回调RemoteAnimationController::FinishedCallback ‘
那么现在需要弄明白动画结束回调执行的是哪个回调,和launcher进程是怎么触发结束回调的。
2.1 system_server 对结束回调的处理
虽然之前已经代码都提过结束回调,这一小节来重新单独整理一下: 首先是WindowContainer::onAnimationFinished这个回调被传递到SurfaceAnimator中
# SurfaceAnimator
SurfaceAnimator(Animatable animatable,
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
......
mLeash = createAnimationLeash......
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
......
}
在执行startAnimation方法的时候传递的是 mInnerAnimationFinishedCallback ,这个也就是经过一次封装的 WindowContainer::onAnimationFinished 然后下一步执行的是
# RemoteAnimationController.RemoteAnimationAdapterWrapper
// 回调被保存在这个变量中
private OnAnimationFinishedCallback mCapturedFinishCallback;
// 回调就是现在的finishCallback参数
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
......
mCapturedLeash = animationLeash;
// 赋值给mCapturedFinishCallback
mCapturedFinishCallback = finishCallback;
mAnimationType = type;
}
下一步就是 goodToGo 流程触发 launcher 执行动画
# RemoteAnimationController
/
private FinishedCallback mFinishedCallback;
void goodToGo(@WindowManager.TransitionOldType int transit) {
......
mFinishedCallback = new FinishedCallback(this);
......
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
}
这个流程又重新创了个回调,然后传递给了 launcher ,那么 launcher 在动画结束后,肯定是调用的就是这个 FinishedCallback 了,所以先看看这里的 FinishedCallback 和最初的WindowContainer::onAnimationFinished 有什么关系.
# RemoteAnimationController
// 是个binder
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
// 外部类引用
RemoteAnimationController mOuter;
@Override
public void onAnimationFinished() throws RemoteException {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
final long token = Binder.clearCallingIdentity();
try {
if (mOuter != null) {
mOuter.onAnimationFinished();
// In case the client holds on to the finish callback, make sure we don't leak
// RemoteAnimationController which in turn would leak the runner on the client.
mOuter = null;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
private void onAnimationFinished() {
// 打印动画结束log
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
mPendingAnimations.size());
......
try {
// 再次打印log
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"onAnimationFinished(): Notify animation finished:");
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
if (adapters.mAdapter != null) {
// 远端动画结束从这会调到最初的WindowContainer::onAnimationFinished
adapters.mAdapter.mCapturedFinishCallback
.onAnimationFinished(adapters.mAdapter.mAnimationType,
adapters.mAdapter);
}
......
// 远端动画只会执行一个,但是没执行的也会在这里一起触发完成回调
}
......
}......
......
}
原来 RemoteAnimationController::FinishedCallback 内部调用的还是 WindowContainer::onAnimationFinished。 这里看到因为 launcher 执行完动画回调回来肯定是个跨进程通信,所以才再次将动画封装到 binder 类 FinishedCallback中。 那么剩下的就是看 launcher 进程是如何调用了。
2.2 launcher进程的调用
在launcher这边流程刚开始时也就是 LauncherAnimationRunner::onAnimationStart 就将回调放在了 AnimationResult 对象中,关于 AnimationResult 的构造方法如下:
# LauncherAnimationRunner.AnimationResult
private final Runnable mASyncFinishRunnable;
private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
mSyncFinishRunnable = syncFinishRunnable;
mASyncFinishRunnable = asyncFinishRunnable;
}
也就是说动画结束回调被传递给了 mASyncFinishRunnable 变量。然后下一步就是 AppLaunchAnimationRunner::onAnimationStart 。
# QuickstepTransitionManager.AppLaunchAnimationRunner
@Override
public void onCreateAnimation(int transit,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
// 创建个动画集合
AnimatorSet anim = new AnimatorSet();
...... // 忽略从小部件或者recent打开的分支
// 当前分支
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
......
// 监听动画结束回调,执行 system_server 动画结束的回调
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
skipFirstFrame);
}
在这里 anim 经过 composeIconLaunchAnimator 方法后,里面已经有 leash 动画的 Animator 了。然后调用 AnimationResult::setAnimation。
# LauncherAnimationRunner.AnimationResult
@UiThread
public void setAnimation(AnimatorSet animation, Context context,
@Nullable Runnable onCompleteCallback, boolean skipFirstFrame) {
......
// leash 动画
mAnimator = animation;
......
......
// 设置动画结束监听
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// leash动画结束,触发回调
finish();
}
});
mAnimator.start();
......
}
@UiThread
private void finish() {
if (!mFinished) {
mSyncFinishRunnable.run();
UI_HELPER_EXECUTOR.execute(() -> {
// 重点* 远端动画结束,触发WMS回调
mASyncFinishRunnable.run();
if (mOnCompleteCallback != null) {
MAIN_EXECUTOR.execute(mOnCompleteCallback);
}
});
mFinished = true;
}
}
根据这段代码,AnimationResult 内会持有 leash 动画,然后设置动静结束监听,动画结束后执行回调,然后跨进程回调到 system_server 进程,最终触发构造 SurfaceAnimator 时传递的 WindowContainer::onAnimationFinished。