Android T 远程动画显示流程其三——桌面侧动画启动到系统侧结束流程

前言

接着前文分析Android T 远程动画显示流程其二
我们通过IRemoteAnimationRunner跨进程通信从系统进程来到了桌面进程,这里是真正动画播放的逻辑。
之后又通过IRemoteAnimationFinishedCallback跨进程通信回到系统进程,处理动画结束时的逻辑。

进入桌面进程启动动画

跨进程通信,实现IRemoteAnimationRunner

代码路径:frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java

public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner.Stub {public abstract void onAnimationStart(@WindowManager.TransitionOldType int transit,RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,RemoteAnimationTarget[] nonApps, Runnable finishedCallback);@Overridepublic final void onAnimationStart(@TransitionOldType int transit,RemoteAnimationTarget[] apps,RemoteAnimationTarget[] wallpapers,RemoteAnimationTarget[] nonApps,final IRemoteAnimationFinishedCallback finishedCallback) {//调用自身抽象方法onAnimationStartonAnimationStart(transit, apps, wallpapers,nonApps, () -> {try {finishedCallback.onAnimationFinished();} catch (RemoteException e) {Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"+ " finished callback", e);}});}......
}

这里传递的参数都是前面RemoteAnimationController.goodToGo方法中获取的值。
transit的值是TRANSIT_OLD_WALLPAPER_CLOSE(12);
app指的是桌面和应用的RemoteAnimationTarget;
wallpapers壁纸的RemoteAnimationTarget;
nonApp非APP类型的RemoteAnimationTarget;
finishedCallback是FinishedCallback对象,这里传递的是调用了其onAnimationFinished()方法。

这方方法调用了自身抽象方法调用自身抽象方法onAnimationStart,onAnimationStart方法真正的实现在LauncherAnimationRunner类中

@TargetApi(Build.VERSION_CODES.P)
public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {......@BinderThreadpublic void onAnimationStart(int transit,RemoteAnimationTarget[] appTargets,RemoteAnimationTarget[] wallpaperTargets,RemoteAnimationTarget[] nonAppTargets,Runnable runnable) {Runnable r = () -> {//退出动画的流程,此时mAnimationResult为空,尚未进入该流程finishExistingAnimation();//创建AnimationResult,传递了两个runnable//() -> mAnimationResult = null,把AnimationResult对象置空//runnable,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinishedmAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);//传递从系统侧调用过来的参数创建动画getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,mAnimationResult);};//根据mStartAtFrontOfQueue的值,执行线程 rif (mStartAtFrontOfQueue) {//将Runnable插入到消息队列的前面,以确保它尽快被执行postAtFrontOfQueueAsynchronously(mHandler, r);} else {//将Runnable异步地插入到消息队列中,它将在队列中的其他消息之后执行。postAsyncCallback(mHandler, r);}}......
}
  • 退出动画的流程
    finishExistingAnimation();

        @UiThreadprivate void finishExistingAnimation() {if (mAnimationResult != null) {mAnimationResult.finish();mAnimationResult = null;}}
    

    根据mAnimationResult是否为空执行finish方法,主要就是执行mASyncFinishRunnable,后续会在动画退出流程中细讲finish方法。

  • 创建AnimationResult
    mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);

        public static final class AnimationResult {......private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {mSyncFinishRunnable = syncFinishRunnable;mASyncFinishRunnable = asyncFinishRunnable;}......}
    

    AnimationResult主要用来返回当前动画播放结果,以便后续执行动画播放完成时的回调(mASyncFinishRunnable)。
    () -> mAnimationResult = null,一个把AnimationResult对象置空的Runnable,保存到mSyncFinishRunnable中;
    runnable,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinished,保存到mASyncFinishRunnable中。

  • 传递从系统侧创建的参数创建动画

    getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,mAnimationResult);
    

    传递了从系统侧创建的参数,并传递了mAnimationResult对象。这里调用的是RemoteAnimationFactory接口中的onCreateAnimation方法。

        /*** Used with LauncherAnimationRunner as an interface for the runner to call back to the* implementation.*/@FunctionalInterfacepublic interface RemoteAnimationFactory {/*** Called on the UI thread when the animation targets are received. The implementation must* call {@link AnimationResult#setAnimation} with the target animation to be run.*/void onCreateAnimation(int transit,RemoteAnimationTarget[] appTargets,RemoteAnimationTarget[] wallpaperTargets,RemoteAnimationTarget[] nonAppTargets,LauncherAnimationRunner.AnimationResult result);......}
    

    在最开始Launcher.startActivitySafely流程中,QuickstepTransitionManager.getActivityLaunchOptions方法中创建了AppLaunchAnimationRunner对象,并作为RemoteAnimationFactory对象传递到了。

            mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
    

    因此我们这里RemoteAnimationFactory的实现,就是在QuickstepTransitionManager.AppLaunchAnimationRunner中。

传递从系统侧创建的参数创建动画

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java

    private class AppLaunchAnimationRunner implements RemoteAnimationFactory {private final View mV;private final RunnableList mOnEndCallback;AppLaunchAnimationRunner(View v, RunnableList onEndCallback) {mV = v;mOnEndCallback = onEndCallback;}@Overridepublic void onCreateAnimation(int transit,RemoteAnimationTarget[] appTargets,RemoteAnimationTarget[] wallpaperTargets,RemoteAnimationTarget[] nonAppTargets,LauncherAnimationRunner.AnimationResult result) {//创建AnimatorSetAnimatorSet anim = new AnimatorSet();//判断桌面的是否已经不在前台boolean launcherClosing =launcherIsATargetWithMode(appTargets, MODE_CLOSING);//检查是否从桌面小部件启动应用final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;//检查是否从最近应用列表启动应用final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);//决定是否跳过动画的第一帧final boolean skipFirstFrame;if (launchingFromWidget) {//从桌面小部件启动应用的动画composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,wallpaperTargets, nonAppTargets, launcherClosing);addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);skipFirstFrame = true;} else if (launchingFromRecents) {//从最近任务启动应用的动画composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,launcherClosing);addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);skipFirstFrame = true;} else {//点击桌面图标启动应用的动画composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,launcherClosing);addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);skipFirstFrame = false;}//桌面不在前台给动画添加一个监听器if (launcherClosing) {anim.addListener(mForceInvisibleListener);}//设置动画和回调result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,skipFirstFrame);}@Overridepublic void onAnimationCancelled() {mOnEndCallback.executeAllAndDestroy();}}

这里我们主要关注点击桌面图标启动应用的动画逻辑

点击桌面图标启动应用的动画

composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,launcherClosing);

anim一个AnimatorSet对象;
mV这里指的是启动的应用图标,比如com.android.launcher3.BubbleTextView{bace738 VFED..CL. ........ 582,525-859,945 #7f09016a app:id/icon}
appTargets指的是桌面和应用的RemoteAnimationTarget;
wallpaperTargets壁纸的RemoteAnimationTarget;
nonAppTargets非APP类型的RemoteAnimationTarget;
launcherClosing此时桌面的是否已经不在前台,因此值为true

    /*** Compose the animations for a launch from the app icon.** @param anim            the animation to add to* @param v               the launching view with the icon* @param appTargets      the list of opening/closing apps* @param launcherClosing true if launcher is closing*/private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,@NonNull RemoteAnimationTarget[] appTargets,@NonNull RemoteAnimationTarget[] wallpaperTargets,@NonNull RemoteAnimationTarget[] nonAppTargets,boolean launcherClosing) {// Set the state animation first so that any state listeners are called// before our internal listeners.//setCurrentAnimation(anim)取消任何正在运行的动画,设置新的动画//即将动画设置为当前状态动画mLauncher.getStateManager().setCurrentAnimation(anim);// Note: the targetBounds are relative to the launcherint startDelay = getSingleFrameMs(mLauncher);//设置动画参数Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);//设置动画启动延时windowAnimator.setStartDelay(startDelay);//设置windowAnimator给AnimatorSet对象anim.play(windowAnimator);//如果桌面已经不在最顶层显示if (launcherClosing) {// Delay animation by a frame to avoid jank.//将动画延迟一帧以避免抖动//创建一个launcherAnimator动画和endListener线程Pair<AnimatorSet, Runnable> launcherContentAnimator =getLauncherContentAnimator(true /* isAppOpening */, startDelay, false);//把launcherAnimator动画放到AnimatorSetanim.play(launcherContentAnimator.first);anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {//运行endListener线程launcherContentAnimator.second.run();}});}}

之前最为关键的就是getOpeningWindowAnimators方法

Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);

这个方法是动画真正的设置部分

设置动画相关参数、监听等

    /*** @return Animator that controls the window of the opening targets from app icons.*/private Animator getOpeningWindowAnimators(View v,RemoteAnimationTarget[] appTargets,RemoteAnimationTarget[] wallpaperTargets,RemoteAnimationTarget[] nonAppTargets,boolean launcherClosing) {//获取应用方向int rotationChange = getRotationChange(appTargets);//获取启动应用的窗口边界Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);//检查appTargets中所有应用目标是否半透明//areAllTargetsTranslucent方法返回的的是,//mode值为MODE_OPENING(正在打开的应用)的RemoteAnimationTarget的isTranslucent的值boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);RectF launcherIconBounds = new RectF();//获取一个浮动图标视图FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,!appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);Rect crop = new Rect();Matrix matrix = new Matrix();//创建mMode为MODE_OPENING的RemoteAnimationTargets对象//把app、壁纸和非app类型的RemoteAnimationTarget对象保存到RemoteAnimationTargets中RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,wallpaperTargets, nonAppTargets, MODE_OPENING);//创建SurfaceTransactionApplier对象SurfaceTransactionApplier surfaceApplier =new SurfaceTransactionApplier(floatingView);//为了确保动画完成时,释放相关资源openingTargets.addReleaseCheck(surfaceApplier);//获取导航栏的RemoteAnimationTarget对象RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();//DragLayer是一个ViewGroup,协调处理它的子view拖动的容器//getLocationOnScreen获取DragLayer在屏幕上的绝对位置int[] dragLayerBounds = new int[2];mDragLayer.getLocationOnScreen(dragLayerBounds);//检查是否支持冷启动窗口Splash Screenfinal boolean hasSplashScreen;if (supportsSSplashScreen()) {int taskId = openingTargets.getFirstAppTargetTaskId();Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);Pair<Integer, Integer> taskParams =mTaskStartParams.getOrDefault(taskId, defaultParams);mTaskStartParams.remove(taskId);hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;} else {hasSplashScreen = false;}//创建AnimOpenProperties对象,设置应用启动时的动画属性AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],hasSplashScreen, floatingView.isDifferentFromAppIcon());//计算裁剪区域的边界int left = prop.cropCenterXStart - prop.cropWidthStart / 2;int top = prop.cropCenterYStart - prop.cropHeightStart / 2;int right = left + prop.cropWidthStart;int bottom = top + prop.cropHeightStart;// Set the crop here so we can calculate the corner radius below.crop.set(left, top, right, bottom);//创建临时矩形和点对象RectF floatingIconBounds = new RectF();RectF tmpRectF = new RectF();Point tmpPos = new Point();//设置动画的一些参数和监听AnimatorSet animatorSet = new AnimatorSet();ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);appAnimator.setDuration(APP_LAUNCH_DURATION);//设置动画的插值器为LINEAR。插值器决定了动画的速度曲线。LINEAR意味着动画将匀速进行appAnimator.setInterpolator(LINEAR);//为appAnimator添加一个动画监听器floatingView。//当动画开始、结束、取消或重复时,floatingView上的相应方法将被调用。appAnimator.addListener(floatingView);appAnimator.addListener(new AnimatorListenerAdapter() {@Override//监听动开始public void onAnimationStart(Animator animation) {//获取LauncherTaskbarUIController的实例LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();//检查是否应该调用shouldShowEdu()if (taskbarController != null && taskbarController.shouldShowEdu()) {// LAUNCHER_TASKBAR_EDUCATION_SHOWING is set to true here, when the education// flow is about to start, to avoid a race condition with other components// that would show something else to the user as soon as the app is opened.//将LAUNCHER_TASKBAR_EDUCATION_SHOWING设置为true,以避免与其他组件发生竞争Settings.Secure.putInt(mLauncher.getContentResolver(),LAUNCHER_TASKBAR_EDUCATION_SHOWING, 1);}}@Override//监听动结束public void onAnimationEnd(Animator animation) {if (v instanceof BubbleTextView) {//我们这里v是BubbleTextView类型//设置控件v保持按下的状态为false((BubbleTextView) v).setStayPressed(false);}//获取LauncherTaskbarUIController的实例LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();if (taskbarController != null) {//调用shouldShowEdu()taskbarController.showEdu();}//释放所有类型的RemoteAnimationTarget对象//包含壁纸、app和非app类型的RemoteAnimationTarget对象openingTargets.release();}});//initialWindowRadius用于设置动画开始时的窗口圆角半径//supportsRoundedCornersOnWindows(mLauncher.getResources()判断桌面是否支持窗口圆角final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())? Math.max(crop.width(), crop.height()) / 2f: 0f;//finalWindowRadius用于设置动画结束时的窗口圆角半径//mDeviceProfile.isMultiWindowMode检查是否处于多窗口模式//getWindowCornerRadius(mLauncher)获取桌面窗口的圆角半径final float finalWindowRadius = mDeviceProfile.isMultiWindowMode? 0 : getWindowCornerRadius(mLauncher);//inalShadowRadius用于设置动画结束时的阴影半径//appTargetsAreTranslucent表示应用目标是否半透明//mMaxShadowRadius最大阴影半径值final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;MultiValueUpdateListener listener = new MultiValueUpdateListener() {//mDx:这个属性表示在动画过程中,X轴上的位移变化。//它从0开始,到prop.dX结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningXInterpolator作为插值器。FloatProp mDx = new FloatProp(0, prop.dX, 0, APP_LAUNCH_DURATION,mOpeningXInterpolator);//这个属性表示在动画过程中,Y轴上的位移变化。//它从0开始,到prop.dY结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。FloatProp mDy = new FloatProp(0, prop.dY, 0, APP_LAUNCH_DURATION,mOpeningInterpolator);//mIconScaleToFitScreen:这个属性表示应用图标在屏幕上的缩放变化。//它从prop.initialAppIconScale开始,到prop.finalAppIconScale结束,//动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, mOpeningInterpolator);//mIconAlpha:这个属性表示应用图标的透明度变化。//它从prop.iconAlphaStart开始,到0结束,//动画的开始延迟为APP_LAUNCH_ALPHA_START_DELAY,时长为APP_LAUNCH_ALPHA_DURATION,//使用线性插值器(LINEAR)。FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION, LINEAR);//mWindowRadius:这个属性表示窗口圆角的半径变化。//它从initialWindowRadius开始,到finalWindowRadius结束,动画时长为APP_LAUNCH_DURATION,//使用mOpeningInterpolator作为插值器。FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,APP_LAUNCH_DURATION, mOpeningInterpolator);//mShadowRadius:这个属性表示阴影的半径变化。//它从0开始,到finalShadowRadius结束,动画时长为APP_LAUNCH_DURATION,//使用mOpeningInterpolator作为插值器。FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,APP_LAUNCH_DURATION, mOpeningInterpolator);//mCropRectCenterX、mCropRectCenterY、mCropRectWidth、mCropRectHeight//这些属性分别表示裁剪矩形的中心X坐标、中心Y坐标、宽度和高度的变化。//它们都有各自的起始值和结束值,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,0, APP_LAUNCH_DURATION, mOpeningInterpolator);FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,0, APP_LAUNCH_DURATION, mOpeningInterpolator);FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,APP_LAUNCH_DURATION, mOpeningInterpolator);FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,APP_LAUNCH_DURATION, mOpeningInterpolator);//这个属性表示导航栏的淡出效果。//它从1开始,到0结束,动画时长为ANIMATION_NAV_FADE_OUT_DURATION,//使用NAV_FADE_OUT_INTERPOLATOR作为插值器。FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,NAV_FADE_OUT_INTERPOLATOR);//mNavFadeIn:这个属性表示导航栏的淡入效果。它从0开始,到1结束,//动画的开始延迟为ANIMATION_DELAY_NAV_FADE_IN,时长为ANIMATION_NAV_FADE_IN_DURATION,//使用NAV_FADE_IN_INTERPOLATOR作为插值器。FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);//动画的更新@Overridepublic void onUpdate(float percent, boolean initOnly) {// Calculate the size of the scaled icon.//计算缩放图标的大小float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);int right = (int) (left + mCropRectWidth.value);int bottom = (int) (top + mCropRectHeight.value);crop.set(left, top, right, bottom);final int windowCropWidth = crop.width();final int windowCropHeight = crop.height();if (rotationChange != 0) {Utilities.rotateBounds(crop, mDeviceProfile.widthPx,mDeviceProfile.heightPx, rotationChange);}// Scale the size of the icon to match the size of the window crop.//缩放图标的大小以匹配窗口裁剪的大小。float scaleX = iconWidth / windowCropWidth;float scaleY = iconHeight / windowCropHeight;float scale = Math.min(1f, Math.max(scaleX, scaleY));float scaledCropWidth = windowCropWidth * scale;float scaledCropHeight = windowCropHeight * scale;float offsetX = (scaledCropWidth - iconWidth) / 2;float offsetY = (scaledCropHeight - iconHeight) / 2;// Calculate the window position to match the icon position.//计算窗口位置以匹配图标位置。tmpRectF.set(launcherIconBounds);tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);tmpRectF.offset(mDx.value, mDy.value);Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;// Calculate the icon position.//计算图标位置floatingIconBounds.set(launcherIconBounds);floatingIconBounds.offset(mDx.value, mDy.value);Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);floatingIconBounds.left -= offsetX;floatingIconBounds.top -= offsetY;floatingIconBounds.right += offsetX;floatingIconBounds.bottom += offsetY;if (initOnly) {// For the init pass, we want full alpha since the window is not yet ready.//使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。  floatingView.update(1f, 255, floatingIconBounds, percent, 0f,mWindowRadius.value * scale, true /* isOpening */);return;}SurfaceTransaction transaction = new SurfaceTransaction();//遍历桌面和启动应用的RemoteAnimationTarget,获取其leash,分别做处理for (int i = appTargets.length - 1; i >= 0; i--) {RemoteAnimationTarget target = appTargets[i];SurfaceProperties builder = transaction.forSurface(target.leash);if (target.mode == MODE_OPENING) {/*** 如果目标模式是MODE_OPENING(打开模式),代码会设置一个矩阵(matrix)来进行缩放和平移操作。* 根据rotationChange的值(可能是表示屏幕旋转的变量),代码会决定如何平移窗口。  * 然后,使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。  * 接着,通过builder.setMatrix等方法设置窗口的矩阵、裁剪区域、透明度、圆角半径和阴影半径。*/matrix.setScale(scale, scale);if (rotationChange == 1) {matrix.postTranslate(windowTransY0,mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));} else if (rotationChange == 2) {matrix.postTranslate(mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));} else if (rotationChange == 3) {matrix.postTranslate(mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),windowTransX0);} else {matrix.postTranslate(windowTransX0, windowTransY0);}floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,mWindowRadius.value * scale, true /* isOpening */);builder.setMatrix(matrix).setWindowCrop(crop).setAlpha(1f - mIconAlpha.value).setCornerRadius(mWindowRadius.value).setShadowRadius(mShadowRadius.value);} else if (target.mode == MODE_CLOSING) {/*** 如果目标模式是MODE_CLOSING(关闭模式),代码会处理关闭动画。* 首先,根据目标的本地边界或位置设置临时位置(tmpPos)。* 然后,根据rotationChange的值,可能需要对裁剪区域(crop)和临时位置进行旋转调整。* 最后,设置窗口的矩阵和裁剪区域,并将透明度设置为1(完全不透明)。*/if (target.localBounds != null) {tmpPos.set(target.localBounds.left, target.localBounds.top);} else {tmpPos.set(target.position.x, target.position.y);}final Rect crop = new Rect(target.screenSpaceBounds);crop.offsetTo(0, 0);if ((rotationChange % 2) == 1) {int tmp = crop.right;crop.right = crop.bottom;crop.bottom = tmp;tmp = tmpPos.x;tmpPos.x = tmpPos.y;tmpPos.y = tmp;}matrix.setTranslate(tmpPos.x, tmpPos.y);builder.setMatrix(matrix).setWindowCrop(crop).setAlpha(1f);}}/*** 如果navBarTarget不为空(即存在导航栏目标),代码会为其设置动画和视图属性。 * 根据`mNavFadeIn.value`的值,决定是淡入还是淡出导航栏。如果淡入值大于起始值,则应用淡入动画;*/if (navBarTarget != null) {SurfaceProperties navBuilder =transaction.forSurface(navBarTarget.leash);if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {matrix.setScale(scale, scale);matrix.postTranslate(windowTransX0, windowTransY0);navBuilder.setMatrix(matrix).setWindowCrop(crop).setAlpha(mNavFadeIn.value);} else {navBuilder.setAlpha(mNavFadeOut.value);}}surfaceApplier.scheduleApply(transaction);}};appAnimator.addUpdateListener(listener);// Since we added a start delay, call update here to init the FloatingIconView properly.//调用MultiValueUpdateListener.update更新动画显示listener.onUpdate(0, true /* initOnly */);// If app targets are translucent, do not animate the background as it causes a visible// flicker when it resets itself at the end of its animation.//appTargetsAreTranslucent,启动的应用为半透明//或 !launcherClosing,桌面在最顶层if (appTargetsAreTranslucent || !launcherClosing) {//仅设置appAnimator给animatorSetanimatorSet.play(appAnimator);} else {//设置appAnimator和getBackgroundAnimator() (背景动画)//用于并行播放animatorSet.playTogether(appAnimator, getBackgroundAnimator());}return animatorSet;}

设置一些动画相关参数和监听,通过MultiValueUpdateListener.update方法更新动画显示。

调用setAnimation设置动画和回调

回到QuickstepTransitionManager.AppLaunchAnimationRunner.onCreateAnimation方法中,继续看到setAnimation方法:

result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,skipFirstFrame);

前面的在getOpeningWindowAnimators方法中设置的动画,通过anim播放

动画的启动与结束

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java

        /*** Sets the animation to play for this app launch* @param skipFirstFrame Iff true, we skip the first frame of the animation.*                       We set to false when skipping first frame causes jank.*/@UiThreadpublic void setAnimation(AnimatorSet animation, Context context,@Nullable Runnable onCompleteCallback, boolean skipFirstFrame) {if (mInitialized) {throw new IllegalStateException("Animation already initialized");}mInitialized = true;mAnimator = animation;mOnCompleteCallback = onCompleteCallback;//如果动画为空,直接调用finish方法,走结束动画流程if (mAnimator == null) {finish();} else if (mFinished) {//mFinished为true,表示动画播放结束// Animation callback was already finished, skip the animation.//调用mAnimator.start()和mAnimator.end()来跳过动画mAnimator.start();mAnimator.end();if (mOnCompleteCallback != null) {mOnCompleteCallback.run();}} else {// Start the animation//添加动画监听mAnimator.addListener(new AnimatorListenerAdapter() {@Override//动画结束时的监听,调用finish()方法public void onAnimationEnd(Animator animation) {finish();}});//开始播放动画mAnimator.start();//如果skipFirstFrame为trueif (skipFirstFrame) {// Because t=0 has the app icon in its original spot, we can skip the// first frame and have the same movement one frame earlier.//调用mAnimator.setCurrentPlayTime()来设置动画的当前播放时间,//该时间为动画总时长与getSingleFrameMs(context)的较小值。//这可以使得应用图标从原始位置开始的移动提前一帧,//因为t=0时应用图标位于其原始位置。mAnimator.setCurrentPlayTime(Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));}}}}

这个方法主要是通过mAnimator.start();启动动画的播放。当动画播放结束时,使用finish();方法进入动画结束播放流程。

动画播放结束

动画播放结束时,调用finish方法进入结束动画流程

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java

    public static final class AnimationResult {......@UiThreadprivate void finish() {if (!mFinished) {//运行的是 () -> mAnimationResult = null//即把AnimationResult对象置空mSyncFinishRunnable.run();UI_HELPER_EXECUTOR.execute(() -> {//运行的是IRemoteAnimationFinishedCallback.onAnimationFinishedmASyncFinishRunnable.run();if (mOnCompleteCallback != null) {MAIN_EXECUTOR.execute(mOnCompleteCallback);}});//mFinished标志位置为true,表示动画播放完成。mFinished = true;}}......}

前面跨进程通信时,对AnimationResult构造方法进行了初始化

private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {mSyncFinishRunnable = syncFinishRunnable;mASyncFinishRunnable = asyncFinishRunnable;}

并且onAnimationStart方法中给创建了AnimationResult对象,传递了两个runnable。
mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
这里传递的runnable就是跨进程通信传递过来的动画完成时回调。

mSyncFinishRunnable表示的就是() -> mAnimationResult = null,置空AnimationResult对象;
mASyncFinishRunnable表示的就是IRemoteAnimationFinishedCallback.onAnimationFinished方法,即跨进程调用结束动画流程。

跨进程通信进入动画结束流程

代码路径:frameworks/base/core/java/android/view/IRemoteAnimationFinishedCallback.aidl

/*** Interface to be invoked by the controlling process when a remote animation has finished.** @see IRemoteAnimationRunner* {@hide}*/
oneway interface IRemoteAnimationFinishedCallback {@UnsupportedAppUsagevoid onAnimationFinished();
}

IRemoteAnimationFinishedCallback的实现在RemoteAnimationController.FinishedCallback类中

进入系统进程结束动画

跨进程通信,实现IRemoteAnimationFinishedCallback

代码路径:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java

    private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {RemoteAnimationController mOuter;FinishedCallback(RemoteAnimationController outer) {mOuter = outer;}@Overridepublic 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);}}/*** Marks this callback as not be used anymore by releasing the reference to the outer class* to prevent memory leak.*/void release() {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);mOuter = null;}};

这段代码的关键就是调用mOuter.onAnimationFinished();

onAnimationFinished方法的实现

private void onAnimationFinished() {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",mPendingAnimations.size());//移除超时回调mHandler.removeCallbacks(mTimeoutRunnable);synchronized (mService.mGlobalLock) {//解除绑定IRemoteAnimationRunnerunlinkToDeathOfRunner();//释放绑定的IRemoteAnimationFinishedCallbackreleaseFinishedCallback();//开启事务mService.openSurfaceTransaction();try {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,"onAnimationFinished(): Notify animation finished:");//app类型动画结束时回调//调用桌面和启动应用的动画结束时回调for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {final RemoteAnimationRecord adapters = mPendingAnimations.get(i);if (adapters.mAdapter != null) {adapters.mAdapter.mCapturedFinishCallback.onAnimationFinished(adapters.mAdapter.mAnimationType,adapters.mAdapter);}if (adapters.mThumbnailAdapter != null) {adapters.mThumbnailAdapter.mCapturedFinishCallback.onAnimationFinished(adapters.mThumbnailAdapter.mAnimationType,adapters.mThumbnailAdapter);}mPendingAnimations.remove(i);ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",adapters.mWindowContainer);}//壁纸类型动画结束时回调for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);adapter.getLeashFinishedCallback().onAnimationFinished(adapter.getLastAnimationType(), adapter);mPendingWallpaperAnimations.remove(i);ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());}//非App类型动画结束时回调for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) {final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i);adapter.getLeashFinishedCallback().onAnimationFinished(adapter.getLastAnimationType(), adapter);mPendingNonAppAnimations.remove(i);ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s",adapter.getWindowContainer());}} catch (Exception e) {Slog.e(TAG, "Failed to finish remote animation", e);throw e;} finally {mService.closeSurfaceTransaction("RemoteAnimationController#finished");}// Reset input for all activities when the remote animation is finished.final Consumer<ActivityRecord> updateActivities =activity -> activity.setDropInputForAnimation(false);mDisplayContent.forAllActivities(updateActivities);}setRunningRemoteAnimation(false);ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");}

我们这里主要关注的是App类型的动画结束流程,这里通过循环,把桌面和启动的应用动画结束时流程逐个调用。这个循环是反向遍历,因此先走的是桌面动画结束时的回调。

adapters.mAdapter.mCapturedFinishCallback.onAnimationFinished(adapters.mAdapter.mAnimationType,adapters.mAdapter);

mCapturedFinishCallback是RemoteAnimationAdapterWrapper对象,它其实就是SurfaceAnimator.getFinishedCallback方法。

在创建动画leash的流程中,SurfaceAnimator.startAnimation方法中有调用mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);,这里把mInnerAnimationFinishedCallback赋值给了RemoteAnimationAdapterWrapper的mCapturedFinishCallbackmInnerAnimationFinishedCallback在SurfaceAnimator的构造方法初始化的值是getFinishedCallback(staticAnimationFinishedCallback),即动画完成时的回调mCapturedFinishCallback对应的就是getFinishedCallback(staticAnimationFinishedCallback)。

所以这里mCapturedFinishCallback.onAnimationFinished调用的,实际是调用就是SurfaceAnimator.getFinishedCallback中匿名的(type, anim) -> {......}

回调处理动画完成的逻辑

这里的流程与本地动画流程相似
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    private OnAnimationFinishedCallback getFinishedCallback(@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {return (type, anim) -> {synchronized (mService.mGlobalLock) {//移除AnimationAdapter对应的SurfaceAnimator,并将这个SurfaceAnimator返回给target//mAnimationTransferMap属于启动窗口的动画场景,这里我们不涉及final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);if (target != null) {//递归调用onAnimationFinished(type, anim),即return (type, anim) -> {......}//直到所有的SurfaceAnimator移除完target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);return;}//检查动画是否已被新动画替换,如果当前动画 (anim) 不等于之前存储的动画 (mAnimation),则不执行后续操作if (anim != mAnimation) {return;}//定义一个名为 resetAndInvokeFinish 的 Runnablefinal Runnable resetAndInvokeFinish = () -> {// We need to check again if the animation has been replaced with a new// animation because the animatable may defer to finish.//再次检查动画是否已被新动画替换,因为可设置动画可能会延迟到完成。if (anim != mAnimation) {return;}//mSurfaceAnimationFinishedCallback是在WindowContainer.startAnimation中赋值的//其传递值为null,最终SurfaceAnimator.startAnimation赋值给mSurfaceAnimationFinishedCallbackfinal OnAnimationFinishedCallback animationFinishCallback =mSurfaceAnimationFinishedCallback;//重置与动画相关的状态reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);//WindowContainer构造方法中给SurfaceAnimator构造方法传递了staticAnimationFinishedCallbackif (staticAnimationFinishedCallback != null) {//当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。//这是一个静态回调,它对通过这个 SurfaceAnimator 启动的所有动画都有效。//回调WindowContainer.onAnimationFinished方法staticAnimationFinishedCallback.onAnimationFinished(type, anim);}//mSurfaceAnimationFinishedCallback的值为null,因此animationFinishCallback的值为nullif (animationFinishCallback != null) {//当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。//这个回调是每个动画(即每个 AnimationAdapter)特有的。//如果在WindowContainer.startAnimation方法中有赋值,//则回调WindowContainer.onAnimationFinished方法animationFinishCallback.onAnimationFinished(type, anim);}};// If both the Animatable and AnimationAdapter requests to be deferred, only the// first one will be called.//如果 mAnimatable 或动画本身请求延迟动画完成,并且它们都没有被延迟,//那么直接执行 resetAndInvokeFinish.run()。否则,延迟执行。if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {resetAndInvokeFinish.run();}//设置动画完成标志,将 mAnimationFinished 设置为 truemAnimationFinished = true;}};}

这个方法主要做了这几件事:

  1. 通过递归的方式移除所有AnimationAdapter对应的SurfaceAnimator
    其中的mAnimationTransferMap在启动窗口流程中,ActivityRecord.addStartingWindow中有调用transferStartingWindow方法,逐步调用到SurfaceAnimator.transferAnimation中进行添加mService.mAnimationTransferMap.put(mAnimation, this);,这里我们不涉及,因此target的值为null
  2. 使用shouldDeferAnimationFinish方法(默认返回false)用来判断是否需要延迟完成动画
  3. 执行resetAndInvokeFinish.run(),调用reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);重置动画相关状态
  4. 最后调用回调通过staticAnimationFinishedCallback.onAnimationFinished(type, anim);,调用WindowContainer.onAnimationFinished方法处理和响应动画完成的逻辑

重置动画相关状态并移除leash

reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    private void reset(Transaction t, boolean destroyLeash) {//移除AnimationAdapter对应的SurfaceAnimatormService.mAnimationTransferMap.remove(mAnimation);mAnimation = null;mSurfaceAnimationFinishedCallback = null;//动画类型置为空mAnimationType = ANIMATION_TYPE_NONE;//屏幕冻结时的快照final SurfaceFreezer.Snapshot snapshot = mSnapshot;mSnapshot = null;if (snapshot != null) {// Reset the mSnapshot reference before calling the callback to prevent circular reset.//如果有屏幕冻结时的快照,取消该动画。//最终会调用到SurfaceAnimationRunner.onAnimationCancelledsnapshot.cancelAnimation(t, !destroyLeash);}if (mLeash == null) {return;}//使用leash存储动画图层mLeashSurfaceControl leash = mLeash;//把动画图层置为空mLeash = null;//移除leashfinal boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);//将mAnimationFinished设置为falsemAnimationFinished = false;if (scheduleAnim) {//leash成功移除后,在WMS中会通过WindowAnimator调度动画,协调各个窗口mService.scheduleAnimationLocked();}}
移除leash

调用removeLeash方法移除leash
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
传递的mAnimatable表示当前窗口,leash就是动画图层,destroyLeash在前面getFinishedCallback流程中传递的值为true

    static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,boolean destroy) {/* log add start*/Slog.i("WindowManager:","removeLeash leash = " + leash , new Exception());/* log add end*///scheduleAnim一个标志位,初始值为false//为true时,走前面reset方法中的mService.scheduleAnimationLocked()流程boolean scheduleAnim = false;//获取当前窗口的SurfaceControlfinal SurfaceControl surface = animatable.getSurfaceControl();//获取当前窗口的父窗口的SurfaceControlfinal SurfaceControl parent = animatable.getParentSurfaceControl();//获取动画图层final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.// Note that we also set this variable to true even if the parent isn't valid anymore, in// order to ensure onAnimationLeashLost still gets called in this case.// If the animation leash is set, and it is different from the removing leash, it means the// surface now has a new animation surface. We don't want to reparent for that.//1.surface不为空//2.curAnimationLeash不为空,且curAnimationLeash等于leash//因此reparent值为truefinal boolean reparent = surface != null && (curAnimationLeash == null|| curAnimationLeash.equals(leash));if (reparent) {ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to original parent: %s for %s",parent, animatable);// We shouldn't really need these isValid checks but we do// b/130364451//判断当前窗口的surface是否有效,以及该窗口的父窗口的图层不为空且有效if (surface.isValid() && parent != null && parent.isValid()) {//把当前窗口图层和其父窗口的图层重新建立父子关系t.reparent(surface, parent);//scheduleAnim置为truescheduleAnim = true;}}//destroy传递过来值为trueif (destroy) {//移除图层t.remove(leash);//scheduleAnim置为truescheduleAnim = true;}if (reparent) {// Make sure to inform the animatable after the surface was reparented (or reparent// wasn't possible, but we still need to invoke the callback)//1.移除和leash相关联的窗口和surface(这个只在前面requiresEdgeExtension为true时逻辑中有涉及)//2.调整surfaceanimatable.onAnimationLeashLost(t);//scheduleAnim置为truescheduleAnim = true;}return scheduleAnim;}
  • 获取当前窗口的图层
    final SurfaceControl surface = animatable.getSurfaceControl();
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        /*** @return The SurfaceControl for this container.*         The SurfaceControl must be valid if non-null.*/@Overridepublic SurfaceControl getSurfaceControl() {return mSurfaceControl;}
    

    直接返回一个SurfaceControl。

  • 获取当前窗口父窗口的图层
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        /** @return The SurfaceControl parent for this containers SurfaceControl.*         The SurfaceControl must be valid if non-null.*/@Overridepublic SurfaceControl getParentSurfaceControl() {final WindowContainer parent = getParent();if (parent == null) {return null;}return parent.getSurfaceControl();}@Overridefinal protected WindowContainer getParent() {return mParent;}
    

    先获取当前窗口的父窗口,在获取父窗口的SurfaceControl。

  • 获取动画图层
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    @Overridepublic SurfaceControl getAnimationLeash() {return mAnimationLeash;}
    

    mAnimationLeash是前面SurfaceAnimator的startAnimation方法中的mAnimatable.onAnimationLeashCreated(t, mLeash);,把mLeash赋值给了mAnimationLeash,因此这个方法获取的是动画图层。

  • 当前窗口图层和其父窗口的图层重新建立父子关系
    t.reparent(surface, parent);
    桌面的SurfaceControl重新认DefaultTaskDsiplayArea的SurfaceControl为父。
    代码路径:frameworks/base/core/java/android/view/SurfaceControl.java

            /*** Re-parents a given layer to a new parent. Children inherit transform (position, scaling)* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the* parent Surface.** @param sc The SurfaceControl to reparent* @param newParent The new parent for the given control.* @return This Transaction*/@NonNullpublic Transaction reparent(@NonNull SurfaceControl sc,@Nullable SurfaceControl newParent) {//检查传入的SurfaceControl对象是否满足某些预设条件checkPreconditions(sc);long otherObject = 0;if (newParent != null) {//检查新父对象是否被释放。如果已经被释放,那么它会抛出异常。newParent.checkNotReleased();//新父对象不为null且未被释放,那么将新父对象的Native对象赋值给otherObject。otherObject = newParent.mNativeObject;}//传入了三个参数:1.当前对象的Native对象 2.被重新设置父对象的SurfaceControl的Native对象 3.新父对象的Native对象。//用于实现重新设置父对象的具体操作。nativeReparent(mNativeObject, sc.mNativeObject, otherObject);//把被重新设置父对象的SurfaceControl和新父对象存储到mReparentedSurfaces这个map中。mReparentedSurfaces.put(sc, newParent);return this;}
    

    前面说过reparent方法中通过mReparentedSurfaces这个ArrayMap临时存储父子关系,key值存储SurfaceControl对象,value为其父SurfaceControl对象(当前窗口的父窗口的SurfaceControl,即DefaultTaskDsiplayArea的SurfaceControl)
    此时leash还没有被释放,DefaultTaskDsiplayArea的SurfaceControl有两个儿子SurfaceControl,(以桌面为例)关系如下图所示:
    在这里插入图片描述

    此时leash逐渐发现不对劲,但是假装不知道
    假如我们后面不执行移除leash图层的操作,那么这个图层一直会保持这个状态挂在DefaultTaskDsiplayArea上和桌面Task共享父亲。

  • 移除动画图层
    t.remove(leash);

            /*** Equivalent to reparent with a null parent, in that it removes* the SurfaceControl from the scene, but it also releases* the local resources (by calling {@link SurfaceControl#release})* after this method returns, {@link SurfaceControl#isValid} will return* false for the argument.** @param sc The surface to remove and release.* @return This transaction* @hide*/@NonNullpublic Transaction remove(@NonNull SurfaceControl sc) {reparent(sc, null);sc.release();return this;}
    

    同样调用了reparent方法,先把需要remove的图层的父图层置空,然后释放。
    过程如下所示:
    在这里插入图片描述
    leash:原来我才是多余的那个,悠悠苍天,何薄于我!

  • 移除和leash相关联的窗口和surface并调整surface
    animatable.onAnimationLeashLost(t);
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        @Overridepublic void onAnimationLeashLost(Transaction t) {mLastLayer = -1;//调用mWmService中的mSurfaceAnimationRunner对象的onAnimationLeashLost方法//用于移除和leash相关联的窗口,这个只在前面requiresEdgeExtension为true时逻辑中有涉及mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);//mAnimationLeash置为空mAnimationLeash = null;mNeedsZBoost = false;//调整其所有child的z-orderreassignLayer(t);//更新Surface位置updateSurfacePosition(t);}
    

    其中mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);mAnimationLeash前面说过就是动画图层。这个只在前面SurfaceAnimationRunner的startAnimation方法中requiresEdgeExtensiontrue时逻辑中有涉及,其为true时才会操作mEdgeExtensions这个ArrayList,这里不讨论。

协调动画显示

在SurfaceAnimator.reset()方法最后调用了mService.scheduleAnimationLocked();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    void scheduleAnimationLocked() {mAnimator.scheduleAnimation();}void scheduleAnimation() {if (!mAnimationFrameCallbackScheduled) {//mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排mAnimationFrameCallbackScheduled = true;//每一帧被绘制时,回调mAnimationFrameCallbackmChoreographer.postFrameCallback(mAnimationFrameCallback);}}

这个方法的主要作用是确保动画帧回调被正确地安排,以便在每一帧绘制时执行,可以确保动画在每一帧都被调用,从而平滑地更新和显示动画。

处理和响应动画完成的逻辑

回到SurfaceAnimator.getFinishedCallback中匿名的onAnimationFinished方法中有调用staticAnimationFinishedCallback.onAnimationFinished(type, anim);处理和响应动画完成的逻辑。

这里的staticAnimationFinishedCallback也是在SurfaceAnimator构造方法中初始化的

  SurfaceAnimator(Animatable animatable,@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,WindowManagerService service) {mAnimatable = animatable;mService = service;mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);}

在WindowContainer构造方法中初始化mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);,因此staticAnimationFinishedCallback.onAnimationFinished对应的就是WindowContainer.onAnimationFinished方法

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    /*** Called when an animation has finished running.*/protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {//主要用于 清空 mSurfaceAnimationSources 列表doAnimationFinished(type, anim);//WindowManagerService中实现onAnimationFinished()//用于唤醒所有等待mGlobalLock对象的线程,确保多个线程能够正确地执行任务mWmService.onAnimationFinished();//将 mNeedsZBoost 设置为 false,表示不再需要Z轴增强mNeedsZBoost = false;}

这个里面又调用了另一个doAnimationFinished(type, anim);

		private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {//mSurfaceAnimationSources中每个容器,做对应的onAnimationFinishedmSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);}//清除动画源列表mSurfaceAnimationSources.clear();if (mDisplayContent != null) {//调用DisplayContent的onWindowAnimationFinished方法//从当前源码上看,主要是针对输入法相关做了一些操作mDisplayContent.onWindowAnimationFinished(this, type);}}

WindowContainer.cancelAnimation方法中调用的doAnimationFinished也是这个方法。

我们这里mSurfaceAnimationSources是保存的是需要做动画的ActivityRecord,即桌面ActivityRecord和启动应用的ActivityRecord。
mSurfaceAnimationSources的值是在前面系统侧动画启动流程中WindowContainer.applyAnimationUnchecked方法中添加的。
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);调用了不同容器onAnimationFinished方法,在ActivityRecord和WindowState中都重写了这个方法。我们这里是远程动画,主要调用的就是ActivityRecord中重写的onAnimationFinished方法。

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

    @Overrideprotected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {super.onAnimationFinished(type, anim);Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");//更新标志位mTransit = TRANSIT_OLD_UNSET;mTransitFlags = 0;//更新应用的布局变化setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,"ActivityRecord");//清除缩略图clearThumbnail();//更新应用的可见性状态setClientVisible(isVisible() || mVisibleRequested);getDisplayContent().computeImeTargetIfNeeded(this);ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s"+ ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",this, reportedVisible, okToDisplay(), okToAnimate(),isStartingWindowDisplayed());// clean up thumbnail windowif (mThumbnail != null) {mThumbnail.destroy();mThumbnail = null;}// WindowState.onExitAnimationDone might modify the children list, so make a copy and then// traverse the copy.//通知子窗口动画结束final ArrayList<WindowState> children = new ArrayList<>(mChildren);children.forEach(WindowState::onExitAnimationDone);// The starting window could transfer to another activity after app transition started, in// that case the latest top activity might not receive exit animation done callback if the// starting window didn't applied exit animation success. Notify animation finish to the// starting window if needed.//通知启动窗口动画结束if (task != null && startingMoved) {final WindowState transferredStarting = task.getWindow(w ->w.mAttrs.type == TYPE_APPLICATION_STARTING);if (transferredStarting != null && transferredStarting.mAnimatingExit&& !transferredStarting.isSelfAnimating(0 /* flags */,ANIMATION_TYPE_WINDOW_ANIMATION)) {transferredStarting.onExitAnimationDone();}}//通知应用过渡动画结束getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);//协调动画显示scheduleAnimation();// Schedule to handle the stopping and finishing activities which the animation is done// because the activities which were animating have not been stopped yet.// 如果需要,调度处理停止和结束活动的任务。这是必要的,因为正在动画的活动可能还没有被停止。mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}

协调动画显示

和前面在SurfaceAnimator.reset()方法最后调用了mService.scheduleAnimationLocked();相似,
这里我们调用的scheduleAnimation();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    /*** Trigger a call to prepareSurfaces from the animation thread, such that pending transactions* will be applied.*/void scheduleAnimation() {mWmService.scheduleAnimationLocked();}

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    void scheduleAnimationLocked() {mAnimator.scheduleAnimation();}

最终调用到了WindowAnimator.scheduleAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java

    void scheduleAnimation() {if (!mAnimationFrameCallbackScheduled) {//mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排mAnimationFrameCallbackScheduled = true;//每一帧被绘制时,回调mAnimationFrameCallbackmChoreographer.postFrameCallback(mAnimationFrameCallback);}}

这个方法的主要作用是确保动画帧回调被正确地安排,以便在每一帧绘制时执行,可以确保动画在每一帧都被调用,从而平滑地更新和显示动画。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/712873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用maven项目引入jQuery

最近在自学 springBoot &#xff0c;期间准备搞一个前后端不分离的东西&#xff0c;于是需要在 maven 中引入jQuery 依赖&#xff0c;网上百度了很多&#xff0c;这里来做一个总结。 1、pom.xml 导入依赖 打开我们项目的 pom.xml 文件&#xff0c;输入以下坐标。这里我使用的是…

FPGA-学会使用vivado中的存储器资源ROM(IP核)

问题&#xff1a; 某芯片,有500个寄存器,需要在上电的时候由FPGA向这些寄存器中写入初始值,初始值已经通过相应的文档给出了具体值,这些值都是已知的。 分析关键点&#xff1a; 数据量比较多&#xff08;Verilog代码&#xff0c;通过case语句、always语句这种查找表的方式,数…

Linux——匿名管道

Linux——匿名管道 什么是管道匿名管道的底层原理观察匿名管道现象读写端的几种情况写端慢&#xff0c;读端快写端快&#xff0c;读端慢 管道的大小写端关闭&#xff0c;读端一直读写端一直写&#xff0c;读端关闭 我们之前一直用的是vim来编写代码&#xff0c;现在有了vscode这…

bert 相似度任务训练,简单版本

目录 任务 代码 train.py predit.py 数据 任务 使用 bert-base-chinese 训练相似度任务&#xff0c;参考&#xff1a;微调BERT模型实现相似性判断 - 知乎 参考他上面代码&#xff0c;他使用的是 BertForNextSentencePrediction 模型&#xff0c;BertForNextSentencePred…

thinkphp学习10-数据库的修改删除

数据修改 使用 update()方法来修改数据&#xff0c;修改成功返回影响行数&#xff0c;没有修改返回 0 public function index(){$data [username > 孙悟空1,];return Db::name(user)->where(id,11)->update($data);}如果修改数据包含了主键信息&#xff0c;比如 i…

STM32标准库开发——BKP备份RTC时钟

备份寄存器BKP(Backup Registers) 由于RTC与BKP关联性较高&#xff0c;所以RTC的时钟校准寄存器以及一些功能都放在了BKP中。TAMPER引脚主要用于防止芯片数据泄露&#xff0c;可以设计一个机关当TAMPER引脚发生电平跳变时自动清除寄存器内数据不同芯片BKP区别&#xff0c;主要体…

c++入门(2)

上期我们说到了部分c修补C语言的不足&#xff0c;今天我们将剩下的一一说清楚。 函数重载 (1).函数重载的形式 C语言不允许函数名相同的同时存在&#xff0c;但是C允许同名函数存在&#xff0c;但是有要求&#xff1a;函数名相同&#xff0c;参数不同&#xff0c;构成函数重…

【数据结构-图论】并查集

并查集&#xff08;Union-Find&#xff09;是一种数据结构&#xff0c;它提供了处理一些不交集的合并及查询问题的高效方法。并查集主要支持两种操作&#xff1a; 查找&#xff08;Find&#xff09;&#xff1a;确定某个元素属于哪个子集&#xff0c;这通常意味着找到该子集的…

人大金仓与mysql的差异与替换

人大金仓中不能使用~下面的符号&#xff0c;字段中使用”&#xff0c;无法识别建表语句 创建表时语句中只定义字段名.字段类型.是否是否为空 Varchar类型改为varchar&#xff08;长度 char&#xff09; Int(0) 类型为int4 定义主键&#xff1a;CONSTRAINT 键名 主键类型&#x…

Found option without preceding group in config file 问题解决

方法就是用记事本打开 然后 左上角点击 文件 有另存为 就可以选择编码格式

Linux设置程序任意位置执行(设置环境变量)

问题 直接编译出来的可执行程序在执行时需要写出完整路径比较麻烦&#xff0c;设置环境变量可以实现在任意位置直接运行。 解决 1.打开.bashrc文件 vim ~/.bashrc 2.修改该文件&#xff08;实现将/home/zhangziheng/file/seqrequester/build/bin&#xff0c;路径下的可执…

第六节:Vben Admin权限-后端控制方式

系列文章目录 第一节:Vben Admin介绍和初次运行 第二节:Vben Admin 登录逻辑梳理和对接后端准备 第三节:Vben Admin登录对接后端login接口 第四节:Vben Admin登录对接后端getUserInfo接口 第五节:Vben Admin权限-前端控制方式 文章目录 系列文章目录前言一、角色权限(后端…

【办公类-18-03】(Python)中班米罗可儿证书批量生成打印(班级、姓名)

作品展示——米罗可儿证书打印幼儿姓名 背景需求 2024年3月1日&#xff0c;中4班孩子一起整理美术操作材料《米罗可儿》的操作本——将每一页纸撕下来&#xff0c;分类摆放、确保纸张上下位置正确。每位孩子们都非常厉害&#xff0c;不仅完成了自己的一本&#xff0c;还将没有…

C++数据结构与算法——二叉搜索树的属性

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

Vue2:路由history模式的项目部署后页面刷新404问题处理

一、问题描述 我们把Vue项目的路由模式&#xff0c;设置成history 然后&#xff0c;build 并把dist中的代码部署到nodeexpress服务中 访问页面后&#xff0c;刷新页面报404问题 二、原因分析 server.js文件 会发现&#xff0c;文件中配置的路径没有Vue项目中对应的路径 所以…

Nacos进阶

目录 Nacos支持三种配置加载方案 Namespace方案 DataID方案 Group方案 同时加载多个配置集 Nacos支持三种配置加载方案 Nacos支持“Namespacegroupdata ID”的配置解决方案。 详情见&#xff1a;Nacos config alibaba/spring-cloud-alibaba Wiki GitHub Namespace方案…

《TCP/IP详解 卷一》第12章 TCP初步介绍

目录 12.1 引言 12.1.1 ARQ和重传 12.1.2 滑动窗口 12.1.3 变量窗口&#xff1a;流量控制和拥塞控制 12.1.4 设置重传的超时值 12.2 TCP的引入 12.2.1 TCP服务模型 12.2.2 TCP可靠性 12.3 TCP头部和封装 12.4 总结 12.1 引言 关于TCP详细内容&#xff0c;原书有5个章…

【C++ map和set】

文章目录 map和set序列式容器和关联式容器键值对setset的主要操作 mapmap主要操作 multiset和multimap map和set 序列式容器和关联式容器 之前我们接触的vector,list,deque等&#xff0c;这些容器统称为序列式容器&#xff0c;其底层为线性序列的的数据结构&#xff0c;里面存…

【LV14 day4 字符设备驱动基础框架】

一、字符设备驱动框架解析 设备的操作函数如果比喻是桩的话&#xff08;性质类似于设备操作函数的函数&#xff0c;在一些场合被称为桩函数&#xff09;&#xff0c;则&#xff1a; 驱动实现设备操作函数 ----------- 做桩 insmod调用的init函数主要作用 --------- 钉桩 rm…

都说了能不动就别动,非要去调整,出生产事故了吧

MyBatis 替换成 MyBatis-Plus 背景介绍 一个老项目&#xff0c;数据库用的是 MySQL 5.7.36 &#xff0c; ORM 框架用的 MyBatis 3.5.0 &#xff0c; mysql-connector-java 版本是 5.1.26 新来了一个干练的小伙&#xff0c;精力充沛&#xff0c;看着就是一个喜欢折腾的主 他…