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.rhkb.cn/news/267523.html

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

相关文章

基于yolov5的电瓶车和自行车检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的电瓶车和自行车检测系统_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的电瓶车和自行车检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&#xff0c;包括代码&#xff0c;数据集&#xff0c;训练好的模型…

svn介绍 4.0

一、svn介绍&#xff08;版本控制工具&#xff09; 1、svn的定义&#xff1a; svn是一个开放源代码的版本控制系统&#xff0c;通过采用分支管理系统的高效管理&#xff0c;简而言之就是用于多个人共同开发同一个项目&#xff0c;实现共享资源&#xff0c;实现最终集中式个管…

2024年sCrypt编程马拉松即将开幕

BSV区块链的建设者们&#xff0c;你们在哪&#xff1f;2024年sCrypt编程马拉松即将拉开帷幕&#xff01; 2024年3月16日至17日&#xff0c;我们将在旧金山市举办一场以比特币智能合约&#xff08;即 sCrypt&#xff09;和比特币通证&#xff08;如Ordinals&#xff09;相结合为…

【蛀牙】日常生活如何正确护理牙齿?刷牙、洗牙、补牙

程序员生活指南之 【蛀牙】日常生活如何正确护理牙齿&#xff1f;刷牙、洗牙、补牙 文章目录 一、日常如何清洗牙齿&#xff1f;——刷牙与洗牙1、牙齿污垢1.1 牙菌斑1.2 软垢1.3 牙结石1.4 牙龈出血 2、如何刷牙2.1 关于时间2.2 各种工具2.3 巴氏刷牙法 二、定期进行洗牙3、如…

链表OJ刷题(二)

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一、链表的回文结构二、相交链表三、链表中倒数第k个节点四、环形链表Ⅰ和Ⅱ总结 前言 一、链表的回文结构 链表的回文结构_牛客题霸_牛客网 这里我们需要先了解一下什么叫做回文&#…

React之组件定义和事件处理

一、组件的分类 在react中&#xff0c;组件分为函数组件和class组件&#xff0c;也就是无状态组件和有状态组件。 * 更过时候我们应该区别使用无状态组件&#xff0c;因为如果有状态组件会触发生命周期所对应的一些函数 * 一旦触发他生命周期的函数&#xff0c;它就会影响当前项…

MQL5学习之RSI指标编写

研究MT5时发现MQL5这个指标编写功能很强大&#xff0c;应该是碾压国内所有的指标系统&#xff0c;不过这个东西相对复杂很多&#xff0c;比通达信公式不知复杂几许&#xff0c;看起来和C语法接近&#xff0c;倒是比较适合自己。试着玩一下&#xff0c;发现还是有点难度的。索性…

修改centos7的dns解决docker拉取镜像超时问题

近期在一台centos7的服务器上部署系统&#xff0c;拉取docker镜像时总是超时&#xff0c;如图所示。网上有教程说&#xff0c;可以修改操纵系统的dns地址&#xff0c;试了一下&#xff0c;果然搞定。 打开dns配置文件 sudo vi /etc/resolv.conf发觉里面的地址设为114.114.114…

Qt篇——QTableWidget保存表格数据到Excel文件中,读Excel内容到QTableWidget

表格和excel例子如下图所示&#xff1a; 一、QTableWidget保存表格数据到Excel文件中 代码如下&#xff1a; &#xff08;pro文件中添加QT axcontainer&#xff09; #include <QAxObject>void MainWindow::saveTableToExcel() {QDateTime current_date_time QDateTi…

交友社交软件开发-php交友聊天系统-

为了开发一个高效的交友系统&#xff0c;需要一个完善的信息管理和筛选机制。这个系统应该能够根据用户的个人信息、兴趣爱好、价值观等标准进行筛选&#xff0c;并向用户提供符合他们要求心仪的人的信息。为了实现这个目标&#xff0c;系统可以利用人工智能技术&#xff0c;分…

经销商文件分发 怎样兼顾安全和效率?

经销商文件分发是指将文件、资料、产品信息等从制造商或经销商传递给经销商的过程。这一过程对于确保经销商能够获取最新的产品信息、销售策略、市场活动资料等至关重要。 想要管理众多经销商合作伙伴之间的文件传输并提高效率&#xff0c;可以采取以下措施&#xff1a; 1、建…

中文文本分类(pytorch 实现)

import torch import torch.nn as nn import torchvision from torchvision import transforms, datasets import os, PIL, pathlib, warningswarnings.filterwarnings("ignore") # 忽略警告信息# win10系统 device torch.device("cuda" if torch.cuda.i…

【yolov8部署实战】VS2019环境下使用Onnxruntime环境部署yolo项目|含源码

一、前言 部署yolo项目&#xff0c;是我这几个月以来做的事情&#xff0c;最近打算把这几个月试过的方法&#xff0c;踩过的坑&#xff0c;以博客的形式&#xff0c;分享一下。关于下面动态中讲到的如何用opencv部署&#xff0c;我在上一篇博客中已经详细讲到了&#xff1a;【…

JavaWeb 自己给服务器安装SQL Server数据库遇到的坑

之前买的虚拟主机免费送了一个SQL Server数据库&#xff0c;由于服务器提供商今年下架我用的那款虚拟主机产品&#xff0c;所以数据库也被收回了。我买了阿里云云服务器&#xff0c;但是没有数据库&#xff0c;于是自己装了一个SQL Server数据库&#xff0c;总结一下遇到的坑。…

el-input组件当数据为空时, 边框变红,并提示错误信息

1&#xff0c;样式 初始&#xff1a; 当不输入口令&#xff0c; 点击确定时&#xff1a; 2, 思路 主要是使用动态类的方式。 先设置输入框变红的样式以及提示文字的样式class 对于样式class 用变量来控制是否奏效。 3&#xff0c; 代码实现 //html&#xff1a; <div cl…

【leetcode】 剑指 Offer学习计划(java版本含注释)(下)

目录 前言第十六天&#xff08;排序&#xff09;剑指 Offer 45. 把数组排成最小的数&#xff08;中等&#xff09;剑指 Offer 61. 扑克牌中的顺子&#xff08;简单&#xff09; 第十七天&#xff08;排序&#xff09;剑指 Offer 40. 最小的k个数&#xff08;简单&#xff09; 第…

NCDA设计大赛获奖作品剖析:UI设计如何脱颖而出?

第十二届大赛简介 - 未来设计师全国高校数字艺术设计大赛&#xff08;NCDA&#xff09;开始啦&#xff01;视觉传达设计命题之一: ui 设计&#xff0c;你想知道的都在这里。为了让大家更好的参加这次比赛&#xff0c;本文特别为大家整理了以往NCDA大赛 UI 设计的优秀获奖作品&a…

QoS简单配置案例

1、两边两个方向做相同的配置&#xff1a;入口复杂流分类用mqc方式配置&#xff0c;ds内设备入口配简单流分类。 2、两边两个方法做拥塞管理配置&#xff0c;拥塞管理配置思路&#xff1a; 拥塞管理的两种配置方法&#xff08;全部用一种也可以&#xff0c;这里学习就用了两种…

算法修炼-动态规划之路径问题(1)

62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;选定一个网格为终点&#xff0c;走到这个网格的所有走法就是这个网格的上面一个网格的所有走法加上这个网格左边一个网格的所有走法&#xff0c;然后做好初始化工作就行。 class Solution { public:int…

Linux线程池

前言 线程池是一种管理线程的机制&#xff0c;它可以在需要时自动创建和销毁线程&#xff0c;以及分配和回收线程资源。线程池的主要优点是减少了频繁创建和销毁线程所带来的开销&#xff0c;提高了系统的稳定性和可扩展性。此外&#xff0c;线程池还可以有效地控制线程的数量&…