本文介绍安卓动画系统中的过渡动画流程。

过渡动画简介

一般过渡动画有在system_server进程中执行的本地动画和在例如systemui等进程执行的远程动画两种情况。

常见的本地动画:App内切换Activity等。

常见的远程动画:从桌面进入App,从App回到桌面,从Notification悬浮窗进入Activity等。

本地动画

从窗口布局流程中RootWindowContainer的performSurfacePlacement方法开始:

    void performSurfacePlacement() {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");try {performSurfacePlacementNoTrace();} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}// "Something has changed!  Let's make it correct now."// TODO: Super crazy long method that should be broken down...void performSurfacePlacementNoTrace() {if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "+ Debug.getCallers(3));int i;if (mWmService.mFocusMayChange) {mWmService.mFocusMayChange = false;mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);}// Initialize state of exiting tokens.final int numDisplays = mChildren.size();for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {final DisplayContent displayContent = mChildren.get(displayNdx);displayContent.setExitingTokensHasVisible(false);}mHoldScreen = null;mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;mUserActivityTimeout = -1;mObscureApplicationContentOnSecondaryDisplays = false;mSustainedPerformanceModeCurrent = false;mWmService.mTransactionSequence++;// TODO(multi-display): recents animation & wallpaper need support multi-display.final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");mWmService.openSurfaceTransaction();try {applySurfaceChangesTransaction();} catch (RuntimeException e) {Slog.wtf(TAG, "Unhandled exception in Window Manager", e);} finally {mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");}mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();checkAppTransitionReady(surfacePlacer);// Defer starting the recents animation until the wallpaper has drawnfinal RecentsAnimationController recentsAnimationController =mWmService.getRecentsAnimationController();if (recentsAnimationController != null) {recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);}for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {final DisplayContent displayContent = mChildren.get(displayNdx);if (displayContent.mWallpaperMayChange) {if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;if (DEBUG_LAYOUT_REPEATS) {surfacePlacer.debugLayoutRepeats("WallpaperMayChange",displayContent.pendingLayoutChanges);}}}if (mWmService.mFocusMayChange) {mWmService.mFocusMayChange = false;mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,false /*updateInputWindows*/);}if (isLayoutNeeded()) {defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",defaultDisplay.pendingLayoutChanges);}handleResizingWindows();if (mWmService.mDisplayFrozen) {ProtoLog.v(WM_DEBUG_ORIENTATION,"With display frozen, orientationChangeComplete=%b",mOrientationChangeComplete);}if (mOrientationChangeComplete) {if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);}mWmService.stopFreezingDisplayLocked();}// Destroy the surface of any windows that are no longer visible.i = mWmService.mDestroySurface.size();if (i > 0) {do {i--;WindowState win = mWmService.mDestroySurface.get(i);win.mDestroying = false;final DisplayContent displayContent = win.getDisplayContent();if (displayContent.mInputMethodWindow == win) {displayContent.setInputMethodWindowLocked(null);}if (displayContent.mWallpaperController.isWallpaperTarget(win)) {displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;}win.destroySurfaceUnchecked();win.mWinAnimator.destroyPreservedSurfaceLocked();} while (i > 0);mWmService.mDestroySurface.clear();}// Time to remove any exiting tokens?for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {final DisplayContent displayContent = mChildren.get(displayNdx);displayContent.removeExistingTokensIfPossible();}for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {final DisplayContent displayContent = mChildren.get(displayNdx);if (displayContent.pendingLayoutChanges != 0) {displayContent.setLayoutNeeded();}}mWmService.setHoldScreenLocked(mHoldScreen);if (!mWmService.mDisplayFrozen) {final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN|| mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);// Post these on a handler such that we don't call into power manager service while// holding the window manager lock to avoid lock contention with power manager lock.mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,0).sendToTarget();mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();}if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;mWmService.mPowerManagerInternal.powerHint(PowerHint.SUSTAINED_PERFORMANCE,(mSustainedPerformanceModeEnabled ? 1 : 0));}if (mUpdateRotation) {ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");mUpdateRotation = updateRotationUnchecked();}if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()|| (mOrientationChangeComplete && !isLayoutNeeded()&& !mUpdateRotation)) {mWmService.checkDrawnWindowsLocked();}final int N = mWmService.mPendingRemove.size();if (N > 0) {if (mWmService.mPendingRemoveTmp.length < N) {mWmService.mPendingRemoveTmp = new WindowState[N + 10];}mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);mWmService.mPendingRemove.clear();ArrayList<DisplayContent> displayList = new ArrayList();for (i = 0; i < N; i++) {final WindowState w = mWmService.mPendingRemoveTmp[i];w.removeImmediately();final DisplayContent displayContent = w.getDisplayContent();if (displayContent != null && !displayList.contains(displayContent)) {displayList.add(displayContent);}}for (int j = displayList.size() - 1; j >= 0; --j) {final DisplayContent dc = displayList.get(j);dc.assignWindowLayers(true /*setLayoutNeeded*/);}}forAllDisplays(dc -> {dc.getInputMonitor().updateInputWindowsLw(true /*force*/);dc.updateSystemGestureExclusion();dc.updateTouchExcludeRegion();});// Check to see if we are now in a state where the screen should// be enabled, because the window obscured flags have changed.mWmService.enableScreenIfNeededLocked();mWmService.scheduleAnimationLocked();// Send any pending task-info changes that were queued-up during a layout defermentmWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");}

调用了RootWindowContainer的checkAppTransitionReady方法:

    private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {// Trace all displays app transition by Z-order for pending layout change.for (int i = mChildren.size() - 1; i >= 0; --i) {final DisplayContent curDisplay = mChildren.get(i);// If we are ready to perform an app transition, check through all of the app tokens// to be shown and see if they are ready to go.if (curDisplay.mAppTransition.isReady()) {// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.curDisplay.mAppTransitionController.handleAppTransitionReady();if (DEBUG_LAYOUT_REPEATS) {surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",curDisplay.pendingLayoutChanges);}}if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {// We have finished the animation of an app transition. To do this, we have// delayed a lot of operations like showing and hiding apps, moving apps in// Z-order, etc.// The app token list reflects the correct Z-order, but the window list may now// be out of sync with it. So here we will just rebuild the entire app window// list. Fun!curDisplay.handleAnimatingStoppedAndTransition();if (DEBUG_LAYOUT_REPEATS) {surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",curDisplay.pendingLayoutChanges);}}}}

调用了AppTransitionController的handleAppTransitionReady方法:

    /*** Handle application transition for given display.*/void handleAppTransitionReady() {mTempTransitionReasons.clear();if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)|| !transitionGoodToGo(mDisplayContent.mChangingContainers,mTempTransitionReasons)) {return;}Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");final AppTransition appTransition = mDisplayContent.mAppTransition;int transit = appTransition.getAppTransition();if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {transit = WindowManager.TRANSIT_UNSET;}mDisplayContent.mSkipAppTransitionAnimation = false;mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();appTransition.removeAppTransitionTimeoutCallbacks();mDisplayContent.mWallpaperMayChange = false;int appCount = mDisplayContent.mOpeningApps.size();for (int i = 0; i < appCount; ++i) {// Clearing the mAnimatingExit flag before entering animation. It's set to true if app// window is removed, or window relayout to invisible. This also affects window// visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the// transition selection depends on wallpaper target visibility.mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();}appCount = mDisplayContent.mChangingContainers.size();for (int i = 0; i < appCount; ++i) {// Clearing for same reason as above.final ActivityRecord activity = getAppFromContainer(mDisplayContent.mChangingContainers.valueAtUnchecked(i));if (activity != null) {activity.clearAnimatingFlags();}}// Adjust wallpaper before we pull the lower/upper target, since pending changes// (like the clearAnimatingFlags() above) might affect wallpaper target result.// Or, the opening app window should be a wallpaper target.mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent.mOpeningApps);// Determine if closing and opening app token sets are wallpaper targets, in which case// special animations are needed.final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)&& hasWallpaperTarget;final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)&& hasWallpaperTarget;transit = maybeUpdateTransitToTranslucentAnim(transit);transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,closingAppHasWallpaper);// Find the layout params of the top-most application window in the tokens, which is// what will control the animation theme. If all closing windows are obscured, then there is// no need to do an animation. This is the case, for example, when this transition is being// done behind a dream window.final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);final ActivityRecord topOpeningApp =getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);final ActivityRecord topClosingApp =getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);final ActivityRecord topChangingApp =getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)|| containsVoiceInteraction(mDisplayContent.mOpeningApps);final int layoutRedo;mService.mSurfaceAnimationRunner.deferStartingAnimations();try {applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,animLp, voiceInteraction);handleClosingApps();handleOpeningApps();handleChangingApps(transit);appTransition.setLastAppTransition(transit, topOpeningApp,topClosingApp, topChangingApp);final int flags = appTransition.getTransitFlags();layoutRedo = appTransition.goodToGo(transit, topOpeningApp,mDisplayContent.mOpeningApps);handleNonAppWindowsInTransition(transit, flags);appTransition.postAnimationCallback();appTransition.clear();} finally {mService.mSurfaceAnimationRunner.continueStartingAnimations();}mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);mDisplayContent.mOpeningApps.clear();mDisplayContent.mClosingApps.clear();mDisplayContent.mChangingContainers.clear();mDisplayContent.mUnknownAppVisibilityController.clear();// This has changed the visibility of windows, so perform// a new layout to get them all up-to-date.mDisplayContent.setLayoutNeeded();mDisplayContent.computeImeTarget(true /* updateImeTarget */);mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(mTempTransitionReasons);if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());});}Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);mDisplayContent.pendingLayoutChanges |=layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;}

其中调用了AppTransitionController的applyAnimations方法:

    /*** Apply an app transition animation based on a set of {@link ActivityRecord}** @param openingApps The list of opening apps to which an app transition animation applies.* @param closingApps The list of closing apps to which an app transition animation applies.* @param transit The current transition type.* @param animLp Layout parameters in which an app transition animation runs.* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice*                         interaction session driving task.*/private void applyAnimations(ArraySet<ActivityRecord> openingApps,ArraySet<ActivityRecord> closingApps, @TransitionType int transit,LayoutParams animLp, boolean voiceInteraction) {if (transit == WindowManager.TRANSIT_UNSET|| (openingApps.isEmpty() && closingApps.isEmpty())) {return;}final ArraySet<WindowContainer> openingWcs = getAnimationTargets(openingApps, closingApps, true /* visible */);final ArraySet<WindowContainer> closingWcs = getAnimationTargets(openingApps, closingApps, false /* visible */);applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,voiceInteraction);applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,voiceInteraction);final AccessibilityController accessibilityController =mDisplayContent.mWmService.mAccessibilityController;if (accessibilityController != null) {accessibilityController.onAppWindowTransitionLocked(mDisplayContent.getDisplayId(), transit);}}/*** Apply animation to the set of window containers.** @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.* @param apps The list of {@link ActivityRecord}s being transitioning.* @param transit The current transition type.* @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes*                invisible.* @param animLp Layout parameters in which an app transition animation runs.* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice*                         interaction session driving task.*/private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,@TransitionType int transit, boolean visible, LayoutParams animLp,boolean voiceInteraction) {final int wcsCount = wcs.size();for (int i = 0; i < wcsCount; i++) {final WindowContainer wc = wcs.valueAt(i);// If app transition animation target is promoted to higher level, SurfaceAnimator// triggers WC#onAnimationFinished only on the promoted target. So we need to take care// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the// app transition.final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();for (int j = 0; j < apps.size(); ++j) {final ActivityRecord app = apps.valueAt(j);if (app.isDescendantOf(wc)) {transitioningDescendants.add(app);}}wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);}}

调用了WindowContainer的applyAnimation方法:

    /*** Applies the app transition animation according the given the layout properties in the* window hierarchy.** @param lp The layout parameters of the window.* @param transit The app transition type indicates what kind of transition to be applied.* @param enter Whether the app transition is entering transition or not.* @param isVoiceInteraction Whether the container is participating in voice interaction or not.* @param sources {@link ActivityRecord}s which causes this app transition animation.** @return {@code true} when the container applied the app transition, {@code false} if the*         app transition is disabled or skipped.** @see #getAnimationAdapter*/boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {if (mWmService.mDisableTransitionAnimation) {ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,"applyAnimation: transition animation is disabled or skipped. "+ "container=%s", this);cancelAnimation();return false;}// Only apply an animation if the display isn't frozen. If it is frozen, there is no reason// to animate and it can cause strange artifacts when we unfreeze the display if some// different animation is running.try {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");if (okToAnimate()) {applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);} else {cancelAnimation();}} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}return isAnimating();}

调用了WindowContainer的applyAnimationUnchecked方法:

    protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,int transit, boolean isVoiceInteraction,@Nullable ArrayList<WindowContainer> sources) {final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,transit, enter, isVoiceInteraction);AnimationAdapter adapter = adapters.first;AnimationAdapter thumbnailAdapter = adapters.second;if (adapter != null) {if (sources != null) {mSurfaceAnimationSources.addAll(sources);}startAnimation(getPendingTransaction(), adapter, !isVisible(),ANIMATION_TYPE_APP_TRANSITION);if (adapter.getShowWallpaper()) {getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;}if (thumbnailAdapter != null) {mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });}}}

首先调用了WindowContainer的getAnimationAdapter方法:

    /*** Gets the {@link AnimationAdapter} according the given window layout properties in the window* hierarchy.** @return The return value will always contain two elements, one for normal animations and the*         other for thumbnail animation, both can be {@code null}.** @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord* @See LocalAnimationAdapter*/Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,int transit, boolean enter, boolean isVoiceInteraction) {final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;......if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {......} else if (isChanging) {......} else {mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);if (a != null) {// Only apply corner radius to animation if we're not in multi window mode.// We don't want rounded corners when in pip or split screen.final float windowCornerRadius = !inMultiWindowMode()? getDisplayContent().getWindowCornerRadius(): 0;AnimationAdapter adapter = new LocalAnimationAdapter(new WindowAnimationSpec(a, mTmpPoint, mTmpRect,getDisplayContent().mAppTransition.canSkipFirstFrame(),appStackClipMode, true /* isAppAnimation */, windowCornerRadius),getSurfaceAnimationRunner());resultAdapters = new Pair<>(adapter, null);mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP|| AppTransition.isClosingTransit(transit);mTransit = transit;mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();} else {resultAdapters = new Pair<>(null, null);}}return resultAdapters;}

创建了LocalAnimationAdapter作为AnimationAdapter。

WindowContainer的applyAnimationUnchecked后面还调用了WindowContainer的startAnimation方法:

    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,@AnimationType int type) {startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);}/*** Starts an animation on the container.** @param anim The animation to run.* @param hidden Whether our container is currently hidden. TODO This should use isVisible at*               some point but the meaning is too weird to work for all containers.* @param type The type of animation defined as {@link AnimationType}.* @param animationFinishedCallback The callback being triggered when the animation finishes.*/void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,@AnimationType int type,@Nullable OnAnimationFinishedCallback animationFinishedCallback) {if (DEBUG_ANIM) {Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);}// TODO: This should use isVisible() but because isVisible has a really weird meaning at// the moment this doesn't work for all animatable window containers.mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,mSurfaceFreezer);}

最后在SurfaceAnimator的startAnimation方法中创建Leash并调用Animatable(WindowContainer)的onAnimationLeashCreated方法和AnimationAdapter(LocalAnimationAdapter)的startAnimation方法来开始执行动画。

远程动画

以在桌面点击悬浮窗形式的Notification打开Activity为例,整个动画流程是这样的:

1 在systemui进程中接收点击事件,创建RemoteAnimationAdapter

从StatusBarNotificationActivityStarter的onNotificationClicked方法开始:

    /*** Called when a notification is clicked.** @param sbn notification that was clicked* @param row row for that notification*/@Overridepublic void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {......ActivityStarter.OnDismissAction postKeyguardAction =() -> handleNotificationClickAfterKeyguardDismissed(sbn, row, controller, intent,isActivityIntent, wasOccluded, showOverLockscreen);if (showOverLockscreen) {mIsCollapsingToShowActivityOverLockscreen = true;postKeyguardAction.onDismiss();} else {mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null /* cancel */, afterKeyguardGone);}}

在Keyguard消失后会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterKeyguardDismissed方法:

    private boolean handleNotificationClickAfterKeyguardDismissed(StatusBarNotification sbn,ExpandableNotificationRow row,RemoteInputController controller,PendingIntent intent,boolean isActivityIntent,boolean wasOccluded,boolean showOverLockscreen) {......final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(sbn, row, controller, intent,isActivityIntent, wasOccluded, parentToCancelFinal);if (showOverLockscreen) {mShadeController.addPostCollapseAction(runnable);mShadeController.collapsePanel(true /* animate */);} else if (mKeyguardStateController.isShowing()&& mStatusBar.isOccluded()) {mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);mShadeController.collapsePanel();} else {mBackgroundHandler.postAtFrontOfQueue(runnable);}return !mNotificationPanel.isFullyCollapsed();}

最终会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterPanelCollapsed方法:

    private void handleNotificationClickAfterPanelCollapsed(StatusBarNotification sbn,ExpandableNotificationRow row,RemoteInputController controller,PendingIntent intent,boolean isActivityIntent,boolean wasOccluded,NotificationEntry parentToCancelFinal) {......startNotificationIntent(intent, fillInIntent, entry, row, wasOccluded, isActivityIntent);......}

调用了StatusBarNotificationActivityStarter的startNotificationIntent方法:

    private void startNotificationIntent(PendingIntent intent,Intent fillInIntent,NotificationEntry entry,View row,boolean wasOccluded,boolean isActivityIntent) {RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,wasOccluded);mLogger.logStartNotificationIntent(entry.getKey(), intent);try {if (adapter != null) {ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(intent.getCreatorPackage(), adapter);}int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,null, null, getActivityOptions(adapter));mMainThreadHandler.post(() -> {mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);});} catch (RemoteException | PendingIntent.CanceledException e) {// the stack trace isn't very helpful here.// Just log the exception message.mLogger.logSendingIntentFailed(e);// TODO: Dismiss Keyguard.}}

调用了ActivityLaunchAnimator的getLaunchAnimation方法:

    public RemoteAnimationAdapter getLaunchAnimation(View sourceView, boolean occluded) {if (!(sourceView instanceof ExpandableNotificationRow)|| !mCallback.areLaunchAnimationsEnabled() || occluded) {return null;}AnimationRunner animationRunner = new AnimationRunner((ExpandableNotificationRow) sourceView);return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);}

其中AnimationRunner是ActivityLaunchAnimator的内部类,其继承了IRemoteAnimationRunner.Stub:

    class AnimationRunner extends IRemoteAnimationRunner.Stub {private final ExpandableNotificationRow mSourceNotification;private final ExpandAnimationParameters mParams;private final Rect mWindowCrop = new Rect();private final float mNotificationCornerRadius;private float mCornerRadius;private boolean mIsFullScreenLaunch = true;private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;public AnimationRunner(ExpandableNotificationRow sourceNofitication) {mSourceNotification = sourceNofitication;mParams = new ExpandAnimationParameters();mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),mSourceNotification.getCurrentBottomRoundness());}@Overridepublic void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,RemoteAnimationTarget[] remoteAnimationWallpaperTargets,IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)throws RemoteException {mMainExecutor.execute(() -> {RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(remoteAnimationTargets);if (primary == null) {setAnimationPending(false);invokeCallback(iRemoteAnimationFinishedCallback);mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);return;}setExpandAnimationRunning(true);mIsFullScreenLaunch = primary.position.y == 0&& primary.sourceContainerBounds.height()>= mNotificationPanel.getHeight();if (!mIsFullScreenLaunch) {mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);}ValueAnimator anim = ValueAnimator.ofFloat(0, 1);mParams.startPosition = mSourceNotification.getLocationOnScreen();mParams.startTranslationZ = mSourceNotification.getTranslationZ();mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();if (mSourceNotification.isChildInGroup()) {int parentClip = mSourceNotification.getNotificationParent().getClipTopAmount();mParams.parentStartClipTopAmount = parentClip;// We need to calculate how much the child is clipped by the parent// because children always have 0 clipTopAmountif (parentClip != 0) {float childClip = parentClip- mSourceNotification.getTranslationY();if (childClip > 0.0f) {mParams.startClipTopAmount = (int) Math.ceil(childClip);}}}int targetWidth = primary.sourceContainerBounds.width();// If the notification panel is collapsed, the clip may be larger than the height.int notificationHeight = Math.max(mSourceNotification.getActualHeight()- mSourceNotification.getClipBottomAmount(), 0);int notificationWidth = mSourceNotification.getWidth();anim.setDuration(ANIMATION_DURATION);anim.setInterpolator(Interpolators.LINEAR);anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mParams.linearProgress = animation.getAnimatedFraction();float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(mParams.linearProgress);int newWidth = (int) MathUtils.lerp(notificationWidth,targetWidth, progress);mParams.left = (int) ((targetWidth - newWidth) / 2.0f);mParams.right = mParams.left + newWidth;mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],primary.position.y, progress);mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]+ notificationHeight,primary.position.y + primary.sourceContainerBounds.bottom,progress);mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,mWindowCornerRadius, progress);applyParamsToWindow(primary);applyParamsToNotification(mParams);applyParamsToNotificationShade(mParams);}});anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {setExpandAnimationRunning(false);invokeCallback(iRemoteAnimationFinishedCallback);}});anim.start();setAnimationPending(false);});}private void invokeCallback(IRemoteAnimationFinishedCallback callback) {try {callback.onAnimationFinished();} catch (RemoteException e) {e.printStackTrace();}}private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(RemoteAnimationTarget[] remoteAnimationTargets) {RemoteAnimationTarget primary = null;for (RemoteAnimationTarget app : remoteAnimationTargets) {if (app.mode == RemoteAnimationTarget.MODE_OPENING) {primary = app;break;}}return primary;}private void setExpandAnimationRunning(boolean running) {mNotificationPanel.setLaunchingNotification(running);mSourceNotification.setExpandAnimationRunning(running);mNotificationShadeWindowViewController.setExpandAnimationRunning(running);mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);mAnimationRunning = running;if (!running) {mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);applyParamsToNotification(null);applyParamsToNotificationShade(null);}}private void applyParamsToNotificationShade(ExpandAnimationParameters params) {mNotificationContainer.applyExpandAnimationParams(params);mNotificationPanel.applyExpandAnimationParams(params);mDepthController.setNotificationLaunchAnimationParams(params);}private void applyParamsToNotification(ExpandAnimationParameters params) {mSourceNotification.applyExpandAnimationParams(params);}private void applyParamsToWindow(RemoteAnimationTarget app) {Matrix m = new Matrix();m.postTranslate(0, (float) (mParams.top - app.position.y));mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());SurfaceParams params = new SurfaceParams.Builder(app.leash).withAlpha(1f).withMatrix(m).withWindowCrop(mWindowCrop).withLayer(app.prefixOrderIndex).withCornerRadius(mCornerRadius).withVisibility(true).build();mSyncRtTransactionApplier.scheduleApply(params);}@Overridepublic void onAnimationCancelled() throws RemoteException {mMainExecutor.execute(() -> {setAnimationPending(false);mCallback.onLaunchAnimationCancelled();});}};

在StatusBarNotificationActivityStarter的startNotificationIntent方法中,创建好了RemoteAnimationAdapter后,通过binder调用了ActivityTaskManagerService的registerRemoteAnimationForNextActivityStart方法:

    @Overridepublic void registerRemoteAnimationForNextActivityStart(String packageName,RemoteAnimationAdapter adapter) {mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"registerRemoteAnimationForNextActivityStart");adapter.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid());synchronized (mGlobalLock) {final long origId = Binder.clearCallingIdentity();try {getActivityStartController().registerRemoteAnimationForNextActivityStart(packageName, adapter);} finally {Binder.restoreCallingIdentity(origId);}}}

调用了ActivityStartController的registerRemoteAnimationForNextActivityStart方法:

    void registerRemoteAnimationForNextActivityStart(String packageName,RemoteAnimationAdapter adapter) {mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);}

调用了PendingRemoteAnimationRegistry的addPendingAnimation方法:

    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();/*** Adds a remote animation to be run for all activity starts originating from a certain package.*/void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {mEntries.put(packageName, new Entry(packageName, adapter));}

最终将RemoteAnimationAdapter保存到了ActivityTaskManagerService.mActivityStartController.mPendingRemoteAnimationRegistry.mEntries中。

2 在ActivityStarter启动Activity时,将RemoteAnimationAdapter保存在ActivityOptions中

在ActivityStarter的executeRequest方法中:

    /*** Executing activity start request and starts the journey of starting an activity. Here* begins with performing several preliminary checks. The normally activity launch flow will* go through {@link #startActivityUnchecked} to {@link #startActivityInner}.*/private int executeRequest(Request request) {......if (request.allowPendingRemoteAnimationRegistryLookup) {checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry().overrideOptionsIfNeeded(callingPackage, checkedOptions);}......final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,callingPackage, callingFeatureId, intent, resolvedType, aInfo,mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,sourceRecord);......}

调用了PendingRemoteAnimationRegistry的overrideOptionsIfNeeded方法:

    /*** Overrides the activity options with a registered remote animation for a certain calling* package if such a remote animation is registered.*/ActivityOptions overrideOptionsIfNeeded(String callingPackage,@Nullable ActivityOptions options) {final Entry entry = mEntries.get(callingPackage);if (entry == null) {return options;}if (options == null) {options = ActivityOptions.makeRemoteAnimation(entry.adapter);} else {options.setRemoteAnimationAdapter(entry.adapter);}mEntries.remove(callingPackage);return options;}

将RemoteAnimationAdapter保存在了ActivityOptions中。

在ActivityStarter的executeRequest方法后面还调用了ActivityRecord的构造器:

    ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,@Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,String _resultWho, int _reqCode, boolean _componentSpecified,boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,ActivityOptions options, ActivityRecord sourceRecord) {......if (options != null) {pendingOptions = options;......}

将这个ActivityOptions保存在了ActivityRecord的成员变量pendingOptions中。

3 ATMS将桌面Pause后,要对新的Activity进行Resume前,检查ActivityOptions时,将RemoteAnimationAdapter保存在了AppTransition的RemoteAnimationController中

先看ActivityTaskManagerService的activityPaused方法:

    @Overridepublic final void activityPaused(IBinder token) {final long origId = Binder.clearCallingIdentity();synchronized (mGlobalLock) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");final ActivityRecord r = ActivityRecord.forTokenLocked(token);if (r != null) {r.activityPaused(false);}Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}Binder.restoreCallingIdentity(origId);}

其中r就是桌面的ActivityRecord,调用了其activityPaused方法:

    void activityPaused(boolean timeout) {if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,"Activity paused: token=" + appToken + ", timeout=" + timeout);final ActivityStack stack = getStack();if (stack != null) {removePauseTimeout();if (stack.mPausingActivity == this) {if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + this+ (timeout ? " (due to timeout)" : " (pause complete)"));mAtmService.deferWindowLayout();try {stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);} finally {mAtmService.continueWindowLayout();}return;} else {EventLogTags.writeWmFailedToPause(mUserId, System.identityHashCode(this),shortComponentName, stack.mPausingActivity != null? stack.mPausingActivity.shortComponentName : "(none)");if (isState(PAUSING)) {callServiceTrackeronActivityStatechange(PAUSED, true);setState(PAUSED, "activityPausedLocked");if (finishing) {if (DEBUG_PAUSE) Slog.v(TAG,"Executing finish of failed to pause activity: " + this);completeFinishing("activityPausedLocked");}}}}mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);}

又调用了其ActivityStack的completePauseLocked方法:

    @VisibleForTestingvoid completePauseLocked(boolean resumeNext, ActivityRecord resuming) {ActivityRecord prev = mPausingActivity;......if (resumeNext) {final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);}......}......}

调用了RootWindowContainer的resumeFocusedStacksTopActivities方法:

    boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {......if (targetStack != null && (targetStack.isTopStackInDisplayArea()|| getTopDisplayFocusedStack() == targetStack)) {result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}......}

调用了ActivityStack的resumeTopActivityUncheckedLocked方法:

    /*** Ensure that the top activity in the stack is resumed.** @param prev The previously resumed activity, for when in the process* of pausing; can be null to call from elsewhere.* @param options Activity options.** @return Returns true if something is being resumed, or false if* nothing happened.** NOTE: It is not safe to call this method directly as it can cause an activity in a*       non-focused stack to be resumed.*       Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the*       right activity for the current system state.*/@GuardedBy("mService")boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {......try {......result = resumeTopActivityInnerLocked(prev, options);......}......}

调用了ActivityStack的resumeTopActivityInnerLocked方法:

    @GuardedBy("mService")private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {......if (anim) {next.applyOptionsLocked();}......}

其中next是要启动的Activity的ActivityRecord,调用了其applyOptionsLocked方法:

    void applyOptionsLocked() {if (pendingOptions != null&& pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {......applyOptionsLocked(pendingOptions, intent);......}}/*** Apply override app transition base on options & animation type.*/void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {final int animationType = pendingOptions.getAnimationType();final DisplayContent displayContent = getDisplayContent();switch (animationType) {......case ANIM_REMOTE_ANIMATION:displayContent.mAppTransition.overridePendingAppTransitionRemote(pendingOptions.getRemoteAnimationAdapter());break;......}}

根据上一步可知,这里的pendingOptions中保存了systemui进程传来的RemoteAnimationAdapter。

最后调用了AppTransition的overridePendingAppTransitionRemote方法:

    void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",isTransitionSet(), remoteAnimationAdapter);if (isTransitionSet()) {clear();mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;mRemoteAnimationController = new RemoteAnimationController(mService,remoteAnimationAdapter, mHandler);}}

在这里创建了RemoteAnimationController,其中保存了RemoteAnimationAdapter。

4 在app过渡动画流程中,使用RemoteAnimationAdapterWrapper作为AnimationAdapter

在WindowContainer的applyAnimationUnchecked方法中调用了WindowContainer的getAnimationAdapter方法:

    /*** Gets the {@link AnimationAdapter} according the given window layout properties in the window* hierarchy.** @return The return value will always contain two elements, one for normal animations and the*         other for thumbnail animation, both can be {@code null}.** @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord* @See LocalAnimationAdapter*/Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,int transit, boolean enter, boolean isVoiceInteraction) {final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;......final RemoteAnimationController controller =getDisplayContent().mAppTransition.getRemoteAnimationController();......// Delaying animation start isn't compatible with remote animations at all.if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {final Rect localBounds = new Rect(mTmpRect);localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);final RemoteAnimationController.RemoteAnimationRecord adapters =controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);}......return resultAdapters;}

其中调用了RemoteAnimationController的createRemoteAnimationRecord方法:

    /*** Creates an animation record for each individual {@link WindowContainer}.** @param windowContainer The windows to animate.* @param position The position app bounds, in screen coordinates.* @param localBounds The bounds of the app relative to its parent.* @param stackBounds The stack bounds of the app relative to position.* @param startBounds The stack bounds before the transition, in screen coordinates* @return The record representing animation(s) to run on the app.*/RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",windowContainer);final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,localBounds, stackBounds, startBounds);mPendingAnimations.add(adapters);return adapters;}

RemoteAnimationRecord是RemoteAnimationController的内部类:

    /*** Contains information about a remote-animation for one WindowContainer. This keeps track of,* potentially, multiple animating surfaces (AdapterWrappers) associated with one* Window/Transition. For example, a change transition has an adapter controller for the* main window and an adapter controlling the start-state snapshot.* <p>* This can be thought of as a bridge between the information that the remote animator sees (via* {@link RemoteAnimationTarget}) and what the server sees (the* {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).*/public class RemoteAnimationRecord {RemoteAnimationAdapterWrapper mAdapter;RemoteAnimationAdapterWrapper mThumbnailAdapter = null;RemoteAnimationTarget mTarget;final WindowContainer mWindowContainer;final Rect mStartBounds;RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,Rect endBounds, Rect startBounds) {mWindowContainer = windowContainer;mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);if (startBounds != null) {mStartBounds = new Rect(startBounds);mTmpRect.set(startBounds);mTmpRect.offsetTo(0, 0);if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {mThumbnailAdapter =new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,mTmpRect);}} else {mStartBounds = null;}}RemoteAnimationTarget createRemoteAnimationTarget() {if (mAdapter == null|| mAdapter.mCapturedFinishCallback == null|| mAdapter.mCapturedLeash == null) {return null;}mTarget = mWindowContainer.createRemoteAnimationTarget(this);return mTarget;}int getMode() {final DisplayContent dc = mWindowContainer.getDisplayContent();final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();// Note that opening/closing transitions are per-activity while changing transitions// are per-task.if (dc.mOpeningApps.contains(topActivity)) {return RemoteAnimationTarget.MODE_OPENING;} else if (dc.mChangingContainers.contains(mWindowContainer)) {return RemoteAnimationTarget.MODE_CHANGING;} else {return RemoteAnimationTarget.MODE_CLOSING;}}}

RemoteAnimationAdapterWrapper是RemoteAnimationController的内部类,其实现了AnimationAdapter接口:

    class RemoteAnimationAdapterWrapper implements AnimationAdapter {private final RemoteAnimationRecord mRecord;SurfaceControl mCapturedLeash;private OnAnimationFinishedCallback mCapturedFinishCallback;private @AnimationType int mAnimationType;final Point mPosition = new Point();final Rect mLocalBounds;final Rect mStackBounds = new Rect();RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,Rect localBounds, Rect stackBounds) {mRecord = record;mPosition.set(position.x, position.y);mLocalBounds = localBounds;mStackBounds.set(stackBounds);}@Overridepublic boolean getShowWallpaper() {return false;}@Overridepublic void startAnimation(SurfaceControl animationLeash, Transaction t,@AnimationType int type, OnAnimationFinishedCallback finishCallback) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");// Restore position and stack crop until client has a chance to modify it.if (mRecord.mStartBounds != null) {t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),mRecord.mStartBounds.height());} else {t.setPosition(animationLeash, mPosition.x, mPosition.y);t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());}mCapturedLeash = animationLeash;mCapturedFinishCallback = finishCallback;mAnimationType = type;}@Overridepublic void onAnimationCancelled(SurfaceControl animationLeash) {if (mRecord.mAdapter == this) {mRecord.mAdapter = null;} else {mRecord.mThumbnailAdapter = null;}if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {mPendingAnimations.remove(mRecord);}if (mPendingAnimations.isEmpty()) {cancelAnimation("allAppAnimationsCanceled");}}@Overridepublic long getDurationHint() {return mRemoteAnimationAdapter.getDuration();}@Overridepublic long getStatusBarTransitionsStartTime() {return SystemClock.uptimeMillis()+ mRemoteAnimationAdapter.getStatusBarTransitionDelay();}@Overridepublic void dump(PrintWriter pw, String prefix) {pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);if (mRecord.mTarget != null) {pw.print(prefix); pw.println("Target:");mRecord.mTarget.dump(pw, prefix + "  ");} else {pw.print(prefix); pw.println("Target: null");}}@Overridepublic void dumpDebug(ProtoOutputStream proto) {final long token = proto.start(REMOTE);if (mRecord.mTarget != null) {mRecord.mTarget.dumpDebug(proto, TARGET);}proto.end(token);}}

5 调用IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画

在AppTransitionController的handleAppTransitionReady方法后面还调用了AppTransition的goodToGo方法:

    /*** @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another*         layout pass needs to be done*/int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {......if (mRemoteAnimationController != null) {mRemoteAnimationController.goodToGo();}......}

调用了RemoteAnimationController的goodToGo方法:

    /*** Called when the transition is ready to be started, and all leashes have been set up.*/void goodToGo() {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");if (mPendingAnimations.isEmpty() || mCanceled) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,"goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",mCanceled, mPendingAnimations.size());onAnimationFinished();return;}// Scale the timeout with the animator scale the controlling app is using.mHandler.postDelayed(mTimeoutRunnable,(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));mFinishedCallback = new FinishedCallback(this);// Create the app targetsfinal RemoteAnimationTarget[] appTargets = createAppAnimations();if (appTargets.length == 0) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");onAnimationFinished();return;}// Create the remote wallpaper animation targets (if any)final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {try {linkToDeathOfRunner();mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,mFinishedCallback);} catch (RemoteException e) {Slog.e(TAG, "Failed to start remote animation", e);onAnimationFinished();}if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");writeStartDebugStatement();}});setRunningRemoteAnimation(true);}

最终调用了IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画。

过渡动画 - 安卓R相关推荐

  1. 转屏动画 - 安卓R

    本文介绍安卓动画系统中的转屏动画流程. 1 旋转的开始 若当前屏幕方向需要旋转,会调用frameworks/base/services/core/java/com/android/server/wm/ ...

  2. Insets动画 - 安卓R

    Insets相关重要类 Insets Insets定义在frameworks/base/graphics/java/android/graphics/Insets.java,其分别定义了窗口上下左右有 ...

  3. 安卓动画系统 - 安卓R

    重要类介绍 Animation Animation定义在frameworks/base/core/java/android/view/animation/Animation.java,其子类有Tran ...

  4. 为什么安卓手机退出大型游戏时没有过渡动画?

    这个问题跟是不是安卓没有关系吧 无论是普通的软件也好,还是游戏也罢,对于手机而言他们都是APP,在根本上并没有太大区别. 如果是在系统层面讲,那么只涉及android系统和ios两个系统关于APP的打 ...

  5. 安卓 activity过渡动画

    Activity过渡的五中实现 1.使用overridePendingTransition方法实现Activity跳转动画  overridePendingTransition方法是Activity中 ...

  6. android过渡动画软件,安卓P过渡动画下载-安卓P过渡动画 v1.0_手机乐园

    软件简介 安卓P过渡动画是一款xp模块,免费的而且安装包也是超级的小,操作起来比较方便,可以从Android P-jfy中进行模块的分离,通过进行定义每一个关键帧的元素属性值进行复杂动画效果的轻松实现 ...

  7. android 动画格式下载,安卓p过渡动画文件下载

    安卓p过渡动画模块是一个非常不错的安卓模块文件,可以帮助大家在切换各种页面和应用的时候更加的流程和美观,安装包非常的小巧,几乎不会占用大家的手机空间,而且免费使用.有喜欢的小伙伴可以来体验! 安卓p过 ...

  8. Android过渡动画,发现掘金小秘密

    android:gravity="center" android:transitionName="textTransform" android:text=&qu ...

  9. android 官方默认动画,Android动画一:Activity过渡动画详细实现原理

    虽然 Android 5.0 之后推出了新的过渡动画方式,但通常只是用于特定的场合使用,activity.overridePendingTransition() 通用方式的过渡动画还是很常用. 原理分 ...

最新文章

  1. 在Ubuntu 16.0.4.5 LTS上安装python 2.7版本的cv2模块
  2. vue中过渡动画(类名实现方式)
  3. 360企业版终端安装说明
  4. 一、Oracle介绍
  5. Android应用开发—setResult()的调用时机
  6. zoj 1586 QSNetwork 最小生成树 Prim Kruskal
  7. 如何在rul中添加图片
  8. UnrealEngine4和3DMax的配合_准备资源
  9. 主从同步redis和mysql的区别_mysql主从复制、redis基础、持久化和主从复制
  10. 用cdr怎么精确裁剪图形
  11. 【Error】Win10/Python3.9安装dlib报错:subprocess.CalledProcessError以及解决方法
  12. 推荐6款神器软件,你用过哪些?
  13. 什么是计算机网络AP,AP与AC的区别是什么,拓扑网络知识。
  14. 试题 算法训练 娜神平衡python
  15. 第十一篇 ANDROID 系统网络连接和管理机制与架构
  16. java实现分页打印功能_分页功能的java实现
  17. vm虚拟机安装lede旁路由_windows Server 2019 虚拟机安装 Openwrt/LEDE 软路由
  18. 摄像头之自动驾驶中的应用
  19. 产销存报表直接在MB5B上取数
  20. 【技术工坊42期】区块链子链技术及墨客链的实现方案

热门文章

  1. php的孪生素数怎么写,张益唐:给出了孪生素数猜想的一个证明方法
  2. 瀑布流ajax思路步骤,原生ajax瀑布流demo分享(必看篇)
  3. Photoshop学习(十五):图层蒙版的应用
  4. SQLServer查询多条件统计总数
  5. shell 脚本中获取执行系统命令的输出结果
  6. 快捷酒店火碱勾兑洗涤剂洗出白床单 7天速8等上榜
  7. 机器人 蓝buff 钩_LOL—蒸汽机器人—布里茨辅助攻略
  8. 小米手环NFC模拟加密门禁卡
  9. extern C _declspec(dllexport)浅析
  10. python-pcl安装教程