Whether it is the animation of Windows in the system, or the animation of a View in the application, the principle is the same. When a window opens, the system adds an animation to each Activity window to make it look easier. There are four things I want to write about animation. First, what are the types of animation? The second animation is how to set, because the window animation and excessive animation (Activity window animation) are not the same, we need to separate, on the Xiaomi mobile phone, you can go to the developer option to slow down the window animation playback speed 5 times or 10 times, you can clearly see the animation process. Third, set animation is completed, how to trigger the vertical refresh signal a ZhenZhen display, because a window there may be multiple animations, such as excessive screen animation, animation, animation window, its animation, and so on, eventually to SurfaceFlinger map display, need to synthesize into a animation, so have a little talk in animation synthesis; Fourth, a simple summary of the application of animation, with a Bessel curve drawing broadcast room praise effect example to explain, in summary, the principle of animation in the system and the application of animation is the same, this article is standing in the perspective of the system, figure out the principle of animation, this article based on Android7.0 source code.
Java defines a number of animation types, each represented by an int value.
Animation types | value | meaning |
---|---|---|
TRANSIT_UNSET | – 1 | Initial value, not set |
TRANSIT_NONE | 0 | No animation |
TRANSIT_ACTIVITY_OPEN | 6 | Open a window at the top of the same task |
TRANSIT_ACTIVITY_CLOSE | 7 | Close the current active window and restore the previous window in the same task |
TRANSIT_TASK_OPEN | 8 | Create a new task and create a window |
TRANSIT_TASK_CLOSE | 9 | Close the current active window and return to the previous task |
TRANSIT_TASK_TO_FRONT | 10 | Move the task to the top |
TRANSIT_TASK_TO_BACK | 11 | Move the current task to the end |
TRANSIT_WALLPAPER_CLOSE | 12 | Close the app without wallpaper |
TRANSIT_WALLPAPER_OPEN | 13 | Start the Wallpaper app |
TRANSIT_WALLPAPER_INTRA_OPEN | 14 | There’s wallpaper open |
TRANSIT_WALLPAPER_INTRA_CLOSE | 15 | There is wallpaper off |
The default is no animation, which is of type TRANSIT_UNSET. For example, when an Activity is started, the system will set a TRANSIT_ACTIVITY_OPEN animation. If you start the Activity component, If the Intent object is FLAG_ACTIVITY_NO_ANIMATION, the system will set TRANSIT_NONE to indicate that no animation is required. If you specify lauchMode, a new Actiivty is created across the Task stack. A TRANSIT_TASK_OPEN type is set, which represents the animation to be used when creating a new task and window. This is similar to when an Activity is closed.
#### two, animation Settings to understand the type of animation, we look at the Activity switch, animation is how to set, first a brief look at the Activity switch.
#####1 Activiy Switch
What is an Activity switch? When the first Activity changes from resume to pause and the second Activity moves to Resume, set the first resume window to invisible and the second resume window to visible.
Switching steps
- The member function startActivityLocked of the ActivityStack class first prepares a switch action for the Activity component that is being started, which you can think of as the type of animation you set earlier.
- The other member functions are then called to tell the previously activated Activity component to go into Paused state.
- Once the previously activated Activity component has reached Paused state, ActivityManagerService checks to see if the process used to run the Activity component that is being started has started. If the process is not already started, ActivityManagerService will start the process and then call realStartActivityLocked, a member of ActivityStack class, to load the Activity component that is being started. And set its state to Resumed. This is divided into two points: setAppVisibility and notify Lauch Activity.
- Finally, inform the WindowManagerService service to perform the switch operation prepared earlier
Let’s start by combing through the prepareAppTransition method. What kind of switching action is set depends on the Activity’s behavior.
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if(! checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,"prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { boolean prepared = mAppTransition.prepareAppTransitionLocked( transit, alwaysKeepCurrent); / / prepared for true that has been successfully set up the switching operation, but the current frozen screen, screen, Display out unprepared, / / set mSkipAppTransitionAnimation equal to zerotrue, to skip the execution of the animation corresponding to the switching operation.if (prepared && okToDisplay()) {
mSkipAppTransitionAnimation = false; }}}Copy the code
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) { ..... //isTransitionSet() indicates that the switching operation type has been setif(! isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {setAppTransition(transit);
} else if(! AlwaysKeepCurrent) {//alwaysKeepCurrent if equaltrue, indicates that the switchover type set last time is maintained, and the new one cannot overwrite itif (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
setAppTransition(transit);
} else if (transit == TRANSIT_ACTIVITY_OPEN
&& isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
// Opening a new activity always supersedes a close for the anim.
setAppTransition(transit);
}
}
boolean prepared = prepare();
if(isTransitionSet()) {// Send a 5 second timeout message to the WMS running thread (android.display thread), indicating that the 5 second time class to complete the switch operation. mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); }return prepared;
}Copy the code
If transit == TRANSIT_TASK_OPEN and isTransitionEqual(TRANSIT_TASK_CLOSE) returns true, SetAppTransition indicates that the Activity was switched to TRANSIT_TASK_CLOSE last time (before), so you can call setAppTransition because open animations have higher priority than closed animations.
If transit == TRANSIT_ACTIVITY_OPEN and isTransitionEqual(TRANSIT_ACTIVITY_CLOSE) returns true, SetAppTransition indicates that the Activity was switched to TRANSIT_ACTIVITY_CLOSE last time (before), so you can call setAppTransition because open animations have higher priority than closed animations.
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
private void setAppTransition(int transit) {
mNextAppTransition = transit;
setLastAppTransition(TRANSIT_UNSET, null, null);
}Copy the code
After the setAppTransition has been executed and the previously activated Activity component has entered the Paused state and the client process has started, At this point, ActivityManagerService will call realStartActivityLocked, a member of ActivityStack, to load the Activity component that is starting and set its state to Resumed, First take a look at setAppVisibility, which sets window visibility.
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public void setAppVisibility(IBinder token, boolean visible) { ..... AppWindowToken wtoken; Synchronized (mWindowMap) {// Use ActivityRecord:Token to find AppWindowToken, Wtoken = findAppWindowToken(token);if (wtoken == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return; }... //mOpeningApps is a member of WMS, which stores the AppWindowToken of all open Windows. //mClosingApps is a member of WMS, which stores the AppWindowToken of all closed Windows. Remove McLosingapps.remove (wToken) from visible; // waiting to display wtoken.waitingToShow =false; wtoken.hiddenRequested = ! visible;if(! visible) { // If the app is deadwhile it was visible, we kept its dead window on screen.
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.
wtoken.removeAllDeadWindows();
wtoken.setVisibleBeforeClientHidden();
} else if (visible) {
if(! mAppTransition.isTransitionSet() && mAppTransition.isReady()) { // Add the app mOpeningAppsif transition is unset but ready. This means
// we're doing a screen freeze, and the unfreeze will wait for all opening // apps to be ready. mOpeningApps.add(wtoken); } wtoken.startingMoved = false; // If the token is currently hidden (should be the common case), or has been // stopped, then we need to set up to wait for its windows to be ready. if (wtoken.hidden || wtoken.mAppStopped) { wtoken.clearAllDrawn(); // If the app was already visible, don'T reset the waitingToShow state. // If hidden equalsfalse, the Activity component is currently invisible. And since visible above istrue, indicating that the Activity will be set to visible, // Therefore, the AppWindowToken object wToken member variable waitingToShow needs to be set totrue.if (wtoken.hidden) {
wtoken.waitingToShow = true;
}
if (wtoken.clientHidden) {
// In the case where we are making an app visible
// but holding off for a transition, we still need
// to tell the client to make its windows visible so
// they get drawn. Otherwise, we will wait on
// performing the transition until all windows have
// been drawn, they never will be, and we are sad.
wtoken.clientHidden = false; // Notifies the application process to set the Activity component described by the parameter token totrue
wtoken.sendAppVisibilityToClients();
}
}
wtoken.requestUpdateWallpaperIfNeeded();
if (DEBUG_ADD_REMOVE) Slog.v(
TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false; } / / thisifThe animation will only go when the animation is set and the screen is not frozen, bright or Display OKif (okToDisplay() && mAppTransition.isTransitionSet()) {
if (wtoken.mAppAnimator.usingTransferredAnimation
&& wtoken.mAppAnimator.animation == null) {
Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
+ ", using null transfered animation!");
}
if(! wtoken.mAppAnimator.usingTransferredAnimation && (! wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Setting dummy animation on: "+ wtoken); / / set dummy animation, can understand the role of is a stance, will set in it behind the real animation wtoken. MAppAnimator. SetDummyAnimation (); } wtoken.inPendingTransaction =true;
if(visible) {// Add wtoken to mOpeningApps mopeningapps.add (wtoken); wtoken.mEnteringAnimation =true;
} else{// Not visible, add wtoken to mClosingApps McLosingapps.add (wtoken); wtoken.mEnteringAnimation =false;
}
if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps. final WindowState win = findFocusedWindowLocked(getDefaultDisplayContentLocked()); if (win ! = null) { final AppWindowToken focusedToken = win.mAppToken; if (focusedToken ! = null) { focusedToken.hidden = true; mOpeningApps.add(focusedToken); } } } return; } final long origId = Binder.clearCallingIdentity(); wtoken.inPendingTransaction = false; // Set the visibility of the Activity component described by parameter token to the value described by parameter visible; setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,true, wtoken.voiceInteraction); / / report to WMS service token parameters described the visibility of the Activity component wtoken. UpdateReportedVisibilityLocked (); Binder.restoreCallingIdentity(origId); }}Copy the code
There are many variables in this method, so it will take some time to understand them all. Once setAppVisibility is set, the client will be notified to start the APP process (so that the window token is determined when an instance of an Activity does not yet exist). The completeResumeLocked method checks from top to bottom which Activity components need to be made visible and which need to be made invisible, finding the first Activity at the top of the stack that is displayed in full screen, The setAppVisibility call is set to true, and the visibility of all Activity components below the full-screen Activity component is set to false. Finally, tell the WindowManagerService service to call the executeAppTransition method to perform the switch we prepared earlier, which is related to the Activity window animation (transition animation), starting with the second section.
#####2. Overanimate Settings
The prepareAppTransition and sendAppVisibility methods set which Activity to hide and which Activity to display. Now start sendAppVisibilityToClient. SendAppVisibilityToClient/dispatchAppVibility these two functions is to inform the upper application window visible change. If the next Activity is cold started, this function does not tell the next Activity window to become visible because it is called before the next Activity window has been added to the WMS. The window addition to the Activity is added to the Activity’s onResume method. WMS. FinishDrawingWindow () is called to notify THE WMS that the window has been drawn by adding a window, measure, layout, or draw. I’m ready to animate. WMS. FinishDrawingWindow () will call WindowStateAnimator. FinishDrawingLocked () update window state mDrawState COMMIT_DRAW_PENDING. The WindowSurfacePlacer requestTraversal method simply sends a DO_TRAVERSAL message to the WMS main thread. The performSurfacePlacement method is executed.
frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
final void performSurfacePlacement() {
if (mDeferDepth > 0) {
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mH.removeMessages(DO_TRAVERSAL);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mWallpaperActionPending = false;
}Copy the code
Sequence diagram performSurfacePlacement, performSurfacePlacementLoop, performSurfacePlacementInner three methods are associated with rendering. PerformSurfacePlacement call performSurfacePlacementLoop, performSurfacePlacementLoop call performSurfacePlacementInner. (Todolist: Comb formSurfacePlacement method)
Step 10 commitFinishDrawingLocked is applySurfaceChangesTransaction method call in, this function will window status to COMMIT_DRAW_PENDING or READY_TO_SHOW window, Update all to the READY_TO_SHOW state
Step 11 updateAllDrawnLocked updates the appWindowToken. allDrawn value. Only all Windows belonging to the AppWindowToken are in the state of drawing completion (generally there is only one window, sometimes there are parent Windows and child Windows, then the number of Windows belonging to the AppWindowToken is more than one). Appwindowtoken. allDrawn is set to true. AppWindowToken. AllDrawn to true will make the WMS in step 10. HandleAppTransitionReadyLocked () full implementation. Step 12 handleAppTransitionReadyLocked mainly do the following several things.
Private int handleAppTransitionReadyLocked (WindowList Windows) {/ / get the number of all open the activity of system int appsCount = mService.mOpeningApps.size(); //transitionGoodToGo can determine multiple typescaseIn the case where no animation is being performed, for example, if a screen-turn animation is being done, any allDrawn in mOpeningApps does not equaltrueEtc.if(! transitionGoodToGo(appsCount)) {return 0;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); Switch/front/get good operation int transit. = mService mAppTransition. GetAppTransition (); / / if you do not succeed because the animation set, or for frozen screen, the cause of the WMS mSkipAppTransitionAnimation fortrueIf so, set the switch operation type to TRANSIT_UNSETif (mService.mSkipAppTransitionAnimation) {
transit = AppTransition.TRANSIT_UNSET;
}
mService.mSkipAppTransitionAnimation = false; mService.mNoAnimationNotifyOnTransitionFinished.clear(); / / set the timeout messages in this time can remove prepareAppTransition mService. MH. RemoveMessages (H.A PP_TRANSITION_TIMEOUT); / / window sort again, prevent out-of-order mService. RebuildAppWindowListLocked (); mWallpaperMayChange =false; // The top-most window will supply the layout params, LayoutParams animLp = null; // We will determine it below. int bestAnimLayer = -1; boolean fullscreenAnim =false; // whether there is voiceInteraction Boolean voiceInteraction =false;
int i;
for (i = 0; i < appsCount; i++) {
final AppWindowToken wtoken = mService.mOpeningApps.valueAt(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. wtoken.clearAnimatingFlags(); } // Adjust wallpaper before we pull the lower/upper target, since pending changes // (like the clearAnimatingFlags() above) might affect wallpaper target result. final DisplayContent displayContent = mService.getDefaultDisplayContentLocked(); if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) ! . = 0 && mWallpaperControllerLocked adjustWallpaperWindows ()) {/ / performed clearAnimatingFlags above, can affect the Z - order. Here to adjust the mService. MLayersController. AssignLayersLocked (Windows); displayContent.layoutNeeded = true; } // If the system is performing a switch between two activities, and both activities need to display the wallpaper, // the window with the lower Z axis will be lowerWallpaperTarget, // Windows with higher z-axis positions are saved in upperWallpaperTarget. final WindowState lowerWallpaperTarget = mWallpaperControllerLocked.getLowerWallpaperTarget(); final WindowState upperWallpaperTarget = mWallpaperControllerLocked.getUpperWallpaperTarget(); boolean openingAppHasWallpaper = false; boolean closingAppHasWallpaper = false; final AppWindowToken lowerWallpaperAppToken; final AppWindowToken upperWallpaperAppToken; if (lowerWallpaperTarget == null) { lowerWallpaperAppToken = upperWallpaperAppToken = null; } else { lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken; upperWallpaperAppToken = upperWallpaperTarget.mAppToken; } // Do a first pass through the tokens for two // things: // (1) Determine if both the closing and opening // app token sets are wallpaper targets, in which // case special animations are needed // (since the wallpaper needs to stay static // behind them). // (2) Find the layout params of the top-most // application window in the tokens, Which is // What will control the animation theme. // Get closed activiy final int closingAppsCount = mService.mClosingApps.size(); / / get number open activiy appsCount = closingAppsCount + mService. MOpeningApps. The size (); / / this code for loop is to switch from participating in operation of the window of the Activity component WindowManager. LayoutParams object selected in a switch to create animation / / requirement is the main window, it was the highest of all candidate window in the Z axis position for (I = 0; i < appsCount; i++) { final AppWindowToken wtoken; if (i < closingAppsCount) { wtoken = mService.mClosingApps.valueAt(i); If (wtoken = = lowerWallpaperAppToken | | wtoken = = upperWallpaperAppToken) {/ / closed the Activity, To display the Wallpaper window closingAppHasWallpaper = true; } } else { wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount); If (wtoken = = lowerWallpaperAppToken | | wtoken = = upperWallpaperAppToken) {/ / open the Activity, OpeningAppHasWallpaper = true; } } voiceInteraction |= wtoken.voiceInteraction; If (wtoken.appFullscreen) { WindowState ws = wtoken.findMainWindow() of type TYPE_BASE_APPLICATION or TYPE_APPLICATION_STARTING; if (ws ! = null) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; fullscreenAnim = true; } } else if (! fullscreenAnim) { WindowState ws = wtoken.findMainWindow(); if (ws ! = null) { if (ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; }}}} / / whether switching operation with wallpaper types related to adjust window type (mayUpdateTransitionToWallpaper) transit = maybeUpdateTransitToWallpaper (transit, openingAppHasWallpaper, closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget); // 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 // the lock screen. if (! mService.mPolicy.allowAppAnimationsLw()) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Animations disallowed by keyguard or dream."); animLp = null; } processApplicationsAnimatingInPlace(transit); mTmpLayerAndToken.token = null; // MIUI ADD: mService.mAppTransition.updateAllowCustomAnimationIfNeeded(mService.mClosingApps); Handle closed Activity handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); // Handle closed Activity handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); final AppWindowToken topClosingApp = mTmpLayerAndToken.token; final int topClosingLayer = mTmpLayerAndToken.layer; // Handle the opened Activity, Final AppWindowToken topOpeningApp = handleOpeningApps(Transit, animLp, voiceInteraction, topClosingLayer); mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); final AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : topOpeningApp.mAppAnimator; final AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : topClosingApp.mAppAnimator; mService.mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator, mService.mOpeningApps, mService.mClosingApps); mService.mAppTransition.postAnimationCallback(); mService.mAppTransition.clear(); mService.mOpeningApps.clear(); mService.mClosingApps.clear(); . return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; }Copy the code
In general, there are roughly eight steps
- 1. Call conditions: Firstly, determine whether timeout occurs. If timeout does not occur, determine whether allDrawn of each AppWindowToken in mOpeningApps is true
- 2. Determine whether the wallpaper needs to be visible. If so, draw the wallpaper first and switch the animation logic before walking
- 3. Remove the timeout message from mAppTransition
- 4, the window stack order rearrangement, rebuildAppWindowListLocked
- 5. Get the mAttr value (LayoutParams) of the top full-screen window and record it in animLp
- 6, whether switching operation with wall paper type, adjust type (mayUpdateTransitionToWallpaper)
- 7, handling handleClosingApps/handleOpeningApps respectively
- 8. Cleanup
The handleOpeningApps function is used to set the visibility of appWindowTok. hidden and to animate the Activity switch (if transit== appTransition.transit_unset, Return true if there is an Activity toggle animation or if the window belonging to the Activity is animating the window. The setTokenVisibilityLocked method is called in handleOpeningApps.
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
if(wtoken.clientHidden == visible) { wtoken.clientHidden = ! visible; / / again notify the application side Settings window visibility wtoken. SendAppVisibilityToClients (); } boolean visibilityChanged =false;
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
(visible && wtoken.waitingForReplacement())) {
boolean changed = false;
boolean runningAppAnimation = false;
if(transit ! = AppTransition.TRANSIT_UNSET) {if(wtoken. MAppAnimator. Animation. = = AppWindowAnimator sDummyAnimation) {/ / to empty the dumb in front of the animation wtoken.mAppAnimator.setNullAnimation(); } // Create an animationif (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}
WindowState window = wtoken.findMainWindow();
if(window ! = null && mAccessibilityController ! = null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onAppWindowTransitionLocked(window, transit); } changed =true; }... }...return delayed;
}Copy the code
Creating animations is what applyAnimationLocked does.
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,
int transit, boolean enter, boolean isVoiceInteraction) {
// 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. Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked"); if (okToDisplay()) { .... // Pass in various parameters, Use AppTransition loadAnimation create an Animation Animation a = mAppTransition. LoadAnimation (lp, transit, enter, mCurConfiguration.uiMode, mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets, isVoiceInteraction, freeform, atoken.mTask.mTaskId, mIsInMultiWindowMode); . if (a ! = null) { .... atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight, mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode()); . } } else { atoken.mAppAnimator.clearAnimation(); } return atoken.mAppAnimator.animation ! = null; }Copy the code
ApplyAnimationLocked is essentially calling loadAnimation internally.
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
int taskId, boolean isInMultiWindowMode) {
if(transit == TRANSIT_WALLPAPER_OPEN && mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM && ! mAllowCustomAnimation) { mNextAppTransitionType = AppTransitionInjector.NEXT_TRANSIT_TYPE_BACK_HOME; } Animation a;if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
|| transit == TRANSIT_TASK_TO_FRONT)) {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_open_enter
: com.android.internal.R.anim.voice_activity_open_exit);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation voice:"
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
|| transit == TRANSIT_TASK_CLOSE
|| transit == TRANSIT_TASK_TO_BACK)) {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_close_enter
: com.android.internal.R.anim.voice_activity_close_exit);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation voice:"
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
a = createRelaunchAnimation(frame, insets);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=" + mNextAppTransition
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
mLauncherAnimationRect.setEmpty();
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter, frame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
frame, transit, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
Slog.v(TAG, "applyAnimation:"
+ " anim=" + a + " nextAppTransition=" + animName
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers="+ Debug.getCallers(3)); }}else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
insets, surfaceInsets, freeform, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
Slog.v(TAG, "applyAnimation:"
+ " anim=" + a + " nextAppTransition=" + animName
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers="+ Debug.getCallers(3)); }}else {
int animAttr = 0;
switch (transit) {
case TRANSIT_ACTIVITY_OPEN:
animAttr = enter
? WindowAnimation_activityOpenEnterAnimation
: WindowAnimation_activityOpenExitAnimation;
break;
case TRANSIT_ACTIVITY_CLOSE:
animAttr = enter
? WindowAnimation_activityCloseEnterAnimation
: WindowAnimation_activityCloseExitAnimation;
break;
case TRANSIT_DOCK_TASK_FROM_RECENTS:
case TRANSIT_TASK_OPEN:
animAttr = enter
? WindowAnimation_taskOpenEnterAnimation
: WindowAnimation_taskOpenExitAnimation;
break;
case TRANSIT_TASK_CLOSE:
animAttr = enter
? WindowAnimation_taskCloseEnterAnimation
: WindowAnimation_taskCloseExitAnimation;
break;
case TRANSIT_TASK_TO_FRONT:
animAttr = enter
? WindowAnimation_taskToFrontEnterAnimation
: WindowAnimation_taskToFrontExitAnimation;
break;
case TRANSIT_TASK_TO_BACK:
animAttr = enter
? WindowAnimation_taskToBackEnterAnimation
: WindowAnimation_taskToBackExitAnimation;
break;
case TRANSIT_WALLPAPER_OPEN:
animAttr = enter
? WindowAnimation_wallpaperOpenEnterAnimation
: WindowAnimation_wallpaperOpenExitAnimation;
break;
case TRANSIT_WALLPAPER_CLOSE:
animAttr = enter
? WindowAnimation_wallpaperCloseEnterAnimation
: WindowAnimation_wallpaperCloseExitAnimation;
break;
case TRANSIT_WALLPAPER_INTRA_OPEN:
animAttr = enter
? WindowAnimation_wallpaperIntraOpenEnterAnimation
: WindowAnimation_wallpaperIntraOpenExitAnimation;
break;
case TRANSIT_WALLPAPER_INTRA_CLOSE:
animAttr = enter
? WindowAnimation_wallpaperIntraCloseEnterAnimation
: WindowAnimation_wallpaperIntraCloseExitAnimation;
break;
case TRANSIT_TASK_OPEN_BEHIND:
animAttr = enter
? WindowAnimation_launchTaskBehindSourceAnimation
: WindowAnimation_launchTaskBehindTargetAnimation;
}
a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a
+ " animAttr=0x" + Integer.toHexString(animAttr)
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers=" + Debug.getCallers(3));
}
return a;
}Copy the code
There is also a lot of code about loadAnimation, but the logic is very simple, that is, according to the set operation type, according to the parameter, Load an Animation using loadAnimationRes() or loadAnimationAttr() or another create Animation interface. There is a priority issue when loading animations.
- If voice interaction is enabled, the corresponding animation is returned based on the mNextAppTransition type. The animation type set by AMS (determined by the Activity Activity) such as TRANSIT_ACTIVITY_OPEN indicates that the Activity is currently started
- If a mNextAppTransitionType is set, the corresponding animation is returned based on that type. Type: set by the client process of animation (determined by the client) as Activity# overridePendingTransition, when deciding to set the animation for the current window, this type of priority is higher than the first.
- Returns the corresponding animation based on the mNextAppTransition type.
- If no condition is met, return null
For example, when the Activity is closed, the animation resource is loaded like this
<set xmlns:android="http://schemas.android.com/apk/res/android" android:zAdjustment="normal">
<alpha android:fromAlpha="0.7" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/linear_out_slow_in"
android:duration="250"/>
</set>Copy the code
When the animation is set up, the setAnimation method is used to store the anim in the AppWindowAnimator member variable animation, which will be retrieved when the animation is executed.
frameworks/base/services/core/java/com/android/server/wm/AppWindowAnimator.java
public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
int stackClip) {
animation = anim;
animating = false;
if(! anim.isInitialized()) { anim.initialize(width, height, width, height); }... }Copy the code
If an animation is set to a mNextAppTransitionType, it will be returned with a higher priority than the first type. The sequence diagram of this process is shown below.
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else{ postAnimationCallback(); }}Copy the code
When mNextAppTransitionType is overridden, animations of this type are preferentially returned when they are created.
#####3. Window animation Settings
boolean commitFinishDrawingLocked() {... mDrawState = READY_TO_SHOW; boolean result =false; final AppWindowToken atoken = mWin.mAppToken; Atoken is null. If atoken is null, it is not an Activity window, and performShowLocked can be called to animate the windowif (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
return result;
}Copy the code
Main method is to call the applyEnterAnimationLocked performShowLocked in creating animation.
void applyEnterAnimationLocked() {
// If we are the new part of a window replacement transition and we have requested
// not to animate, we instead want to make it seamless, so we don't want to apply // an enter transition. if (mWin.mSkipEnterAnimationForSeamlessReplacement) { return; } final int transit; if (mEnterAnimationPending) { mEnterAnimationPending = false; transit = WindowManagerPolicy.TRANSIT_ENTER; } else { transit = WindowManagerPolicy.TRANSIT_SHOW; } applyAnimationLocked(transit, true); . }Copy the code
The value of mEnterAnimationPending equals true, indicating that the currently described window is waiting for display, that is, in the process of being invisible, The WindowManagerService class member functions applyEnterAnimationLocked will set up a type of the window to WindowManagerPolicy TRANSIT_ENTER animation, otherwise, Will set a type of the window to WindowManagerPolicy TRANSIT_SHOW animation. Based on this type, styleable is later determined, converting the value of the parameter transit to a corresponding animation style name.
frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean applyAnimationLocked(int transit, boolean isEntrance) {
......
if (mService.okToDisplay()) {
int anim = mPolicy.selectAnimationLw(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != 0) {
a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
} else {
switch (transit) {
case WindowManagerPolicy.TRANSIT_ENTER:
attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
break;
case WindowManagerPolicy.TRANSIT_EXIT:
attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
break;
case WindowManagerPolicy.TRANSIT_SHOW:
attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
break;
case WindowManagerPolicy.TRANSIT_HIDE:
attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
break;
}
if(attr >= 0) { a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr); }}if (DEBUG_ANIM) Slog.v(TAG,
"applyAnimation: win=" + this
+ " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
+ " a=" + a
+ " transit=" + transit
+ " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
if(a ! = null) {if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
setAnimation(a); mAnimationIsEntrance = isEntrance; }}else {
clearAnimation();
}
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
mService.adjustForImeIfNeeded(mWin.mDisplayContent);
if(isEntrance) { mWin.setDisplayLayoutNeeded(); mService.mWindowPlacerLocked.requestTraversal(); }}returnmAnimation ! = null; }Copy the code
The PhoneWindowManager selectAnimationLw method is called to find the animation type of the special window, which is StatusBar, NavigationBar, or the window type is TYPE_DOCK_DIVIDER. If it’s one of these Windows, it will return an animation type (transit) and save it in anim, and then it will determine if anim is -1, because selectAnimationLw will return -1 if the window is Keyguard or DREAM, if it’s not -1, it’s found, Go back to WindowStateAnimator and use the loadAnimation method of AnimationUtils to find an Animation a stored in the variable A described by the Animation.
If none of the above is true, then determine the attR based on the transit type and call AppTransition’s loadAnimationAttr method to load an animation
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
int anim = 0;
Context context = mContext;
if (animAttr >= 0) {
AttributeCache.Entry ent = getCachedAnimations(lp);
if (ent != null) {
context = ent.context;
anim = ent.array.getResourceId(animAttr, 0);
}
}
if(anim ! = 0) {return AnimationUtils.loadAnimation(context, anim);
}
return null;
}Copy the code
frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
public void setAnimation(Animation anim, long startTime, int stackClip) {
if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ":" + anim);
mAnimating = false;
mLocalAnimating = false; mAnimation = anim; . }Copy the code
Finally, the animation is saved in the WindowStateAnimator member variable mAnimation. In contrast to the previous transitions, the setAnimation method is used to save the anim in the AppWindowAnimator member variable animation. When the animation is executed, the animation is fetched, and the animation’s execution is updated in the next section.