Android P——LockFreeAnimation
Android P中對WMS部分有部分重構,其中一個是無鎖動畫——LockFreeAnimation
LockFreeAnimation對比O以及之前的版本,在系統執行視窗動畫的時候是不需要獲取WMS的大鎖的,這無疑是一件提升系統性能的措施,保證動畫的流暢執行。AMS和WMS的大鎖是除去硬體效能之外軟體層面限制系統性能的一個重要原因,P上的處理方式也提供了一類系統優化的思路。
下面先來對比下P的無鎖動畫對比之前的動畫設計方式存在的優勢,先不看程式碼,從一份桌面啟動應用的systrace直觀感受下

在系統視窗動畫的執行都在WindowAnimator的animate函式中,這個函式的執行是需要獲得WMS的大鎖mWindowMap的
private void animate(long frameTimeNs) { synchronized (mService.mWindowMap) { if (!mInitialized) { return; } // Schedule next frame already such that back-pressure happens continuously scheduleAnimation(); } // Simulate back-pressure by opening and closing an empty animation transaction. This makes // sure that an animation frame is at least presented once on the screen. We do this outside // of the regular transaction such that we can avoid holding the window manager lock in case // we receive back-pressure from SurfaceFlinger. Since closing an animation transaction // without the window manager locks leads to ordering issues (as the transaction will be // processed only at the beginning of the next frame which may result in another transaction // that was executed later in WM side gets executed first on SF side), we don't update any // Surface properties here such that reordering doesn't cause issues. mService.executeEmptyAnimationTransaction(); synchronized (mService.mWindowMap) { ......//這裡執行每一幀的視窗Surfacec變換 }
因此就會存在風險,就是當其他執行緒長時間持有WMS大鎖的時候,視窗動畫的執行就會出現阻塞,導致掉幀。而P上的改進就是為了防止動畫被阻塞,視窗動畫的執行是不需要去拿WMS大鎖的,如下圖所示,為一個P上從桌面啟動應用的systrace。

對比O來看,很明顯動畫由兩部分組成,一個還是原先的android.anim執行緒(也就是WindowAnimator的animate函式中執行),另外一個是android.anima.l(名字應該被截掉了),第二個才是真正執行視窗動畫的執行緒。對比來看藍色方框中的"animating"流程中明顯對比O少了很多幀,而少掉的幀都跑到紅色方框中的"animator"中去執行了,而這個animator中執行視窗動畫是無需獲得WMS大鎖的,所以理論上來講會比原先的方案更優更加流暢。

O中在WindowAnimator的animate函式中紅色方框的部分是執行每個視窗的視窗動畫的地方,所以要想動畫執行,必須要拿到鎖才行。
對比P,WindowAnimator類還保留著,但是拿掉了紅色方框中的程式碼,也就是去掉了其負責視窗動畫的功能。雖然拿掉了視窗動畫,個人理解,這個animate函式還需要負責視窗的旋轉動畫,因此還是保留下來了。
那拿掉了WindowAnimator執行視窗動畫的能力之後,視窗動畫又由誰來負責呢?
對比了P和O的程式碼中wm目錄下的檔案增刪,從這裡出發找找看新的變化
動畫相關的提交從SurfaceAnimator開始
* A class that can run animations on objects that have a set of child surfaces. We do this by * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash * gets attached in the surface hierarchy where the the children were attached to. We then hand off * the Leash to the component handling the animation, which is specified by the * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the * animation will be invoked, at which we reparent the children back to the original parent. */ class SurfaceAnimator {
翻譯: 這個類可以針對那種存在多個child surface的物件進行動畫,在執行動畫的過程中會建立一個沒有Buffer的Surface---“ Leash ”,將所有child surface繫結到leash上,leash同時也會繫結到原先這些child surface繫結的位置。然後我們將leash給到AnimationAdapter去執行動畫,執行動畫結束後會將所有child surface重新繫結到原先的父節點上
97/** 98* Starts an animation. 99* 100* @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the 101*component responsible for running the animation. It runs the animation with 102*{@link AnimationAdapter#startAnimation} once the hierarchy with 103*the Leash has been set up. 104* @param hidden Whether the container holding the child surfaces is currently visible or not. 105*This is important as it will start with the leash hidden or visible before 106*handing it to the component that is responsible to run the animation. 107*/ 108void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) { 109cancelAnimation(t, true /* restarting */, true /* forwardCancel */); 110mAnimation = anim; //step1.先獲取當前需要執行動畫的物件的surface 111final SurfaceControl surface = mAnimatable.getSurfaceControl(); 112if (surface == null) { 113Slog.w(TAG, "Unable to start animation, surface is null or no children."); 114cancelAnimation(); 115return; 116} //step2.使用step1建立的surface來構建一個leash 117mLeash = createAnimationLeash(surface, t, 118mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden); 119mAnimatable.onAnimationLeashCreated(t, mLeash); 120if (mAnimationStartDelayed) { 121if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed"); 122return; 123} //step3.將leash傳給AnimationAdapter,啟動動畫 124mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback); 125}
這個startAnimation的第一個引數是Transaction,所以一定是在開啟一次transaction的情況下開始啟動動畫的
step1 這個getSurfaceControl是Animatable這個Interface中的一個介面,所有能夠執行動畫的物件都需要實現這個interface
401* @return The surface of the object to be animated. 402*/ 403@Nullable SurfaceControl getSurfaceControl();
例如WC的定義,WC是WMS的層次框架的基礎類,也就是WMS中很多物件都具有執行動畫的能力
60class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> 61implements Comparable<WindowContainer>, Animatable {
WC中的實現
1110@Override 1111public SurfaceControl getSurfaceControl() { 1112return mSurfaceControl; 1113}
step2建立Leash(leash的英文就是狗繩的意思,就是用這個leash拴住這些child surface來跑動畫,在SF中這些layer的層次結構不會出現異常)
300private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width, 301int height, boolean hidden) { 302if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); //step2.1.通過makeAnimationLeash構造這個Surface 303final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() 304.setParent(mAnimatable.getAnimationLeashParent()) 305.setName(surface + " - animation-leash") 306.setSize(width, height); 307final SurfaceControl leash = builder.build(); //step2.2.設定transaction相關引數,show和reparent //show:這次transaction中顯示leash //reparent:將leash reparent到當前Surface的父節點上 308if (!hidden) { 309t.show(leash); 310} 311t.reparent(surface, leash.getHandle()); 312return leash; 313}
step2.1建立leash
同樣,例如WC的相關實現 這個流程看樣子會遞迴呼叫到根節點到DisplayContent中
1143@Override 1144public Builder makeAnimationLeash() { 1145return makeSurface(); 1146} 895SurfaceControl.Builder makeSurface() { 896final WindowContainer p = getParent(); 897return p.makeChildSurface(this); 898} 900/** 901* @param child The WindowContainer this child surface is for, or null if the Surface 902*is not assosciated with a WindowContainer (e.g. a surface used for Dimming). 903*/ 904SurfaceControl.Builder makeChildSurface(WindowContainer child) { 905final WindowContainer p = getParent(); 906// Give the parent a chance to set properties. In hierarchy v1 we rely 907// on this to set full-screen dimensions on all our Surface-less Layers. 908return p.makeChildSurface(child) 909.setParent(mSurfaceControl); 910}
在DisplayContent中
3855@Override 3856SurfaceControl.Builder makeChildSurface(WindowContainer child) { 3857SurfaceSession s = child != null ? child.getSession() : getSession(); //通過wms來建立實際的SurfaceControl,然後層層返回,最終這個Surface繫結到最早呼叫啟動動畫的物件上 3858final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s); 3859b.setSize(mSurfaceSize, mSurfaceSize); 3860 3861if (child == null) { 3862return b; 3863} 3864 3865return b.setName(child.getName()) 3866.setParent(mWindowingLayer); 3867}
Step3.使用AnimationAapter啟動動畫
54/** 55* Requests to start the animation. 56* 57* @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an 58*overview of the mechanism. This surface needs to be released by the 59*component running the animation after {@code finishCallback} has been 60*invoked, or after the animation was cancelled. 61* @param t The Transaction to apply the initial frame of the animation. 62* @param finishCallback The callback to be invoked when the animation has finished. 63*/ 64void startAnimation(SurfaceControl animationLeash, Transaction t, 65OnAnimationFinishedCallback finishCallback);
LocalAnimationAdapter就是一個對AnimationAdapter的實現
61@Override 62public void startAnimation(SurfaceControl animationLeash, Transaction t, 63OnAnimationFinishedCallback finishCallback) { 64mAnimator.startAnimation(mSpec, animationLeash, t, 65() -> finishCallback.onAnimationFinished(this)); 66}
這裡用於執行動畫的mAnimator的型別是SurfaceAnimationRunner,在構造AnimationAdapter的時候會傳入SurfaceAnimationRunner,這個類會去執行動畫.下面接著介紹SurfaceAnimationRunner
41LocalAnimationAdapter(AnimationSpec spec, SurfaceAnimationRunner animator) { 42mSpec = spec; 43mAnimator = animator; 44}

從圖可以看出每一個WC對應一個SA,當一個WC需要執行一個特定動畫的時候,只需要傳入一個AD來指定特定的動畫即可,而動畫執行需要的執行緒都是在AD的構造中建立
SurfaceAnimationRunner
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback) { synchronized (mLock) { //step1使用spec,leash,動畫結束後的回撥構造一個RunningAnimation,這個類相當於是一個動畫的記錄 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, finishCallback); mPendingAnimations.put(animationLeash, runningAnim); //step2往編舞者上拋一個回撥 if (!mAnimationStartDeferred) { mChoreographer.postFrameCallback(this::startAnimations); } // Some animations (e.g. move animations) require the initial transform to be applied // immediately. applyTransformation(runningAnim, t, 0 /* currentPlayTime */); } }
從這個來看這個Runner是可以複用的,構造一個然後將多個RunningAnimation放在上面執行,因為Runner中維護了一個列表可以儲存多個Animation

往編舞者上面拋的回撥是startAnimations
230private void startAnimations(long frameTimeNanos) { 231synchronized (mLock) { 232startPendingAnimationsLocked(); 233} 234} 154@GuardedBy("mLock") 155private void startPendingAnimationsLocked() { 156for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 157startAnimationLocked(mPendingAnimations.valueAt(i)); 158} 159mPendingAnimations.clear(); 160}
從上面可以看出,將pending的動畫全部拿出來執行,然後清空掉pending的列表
162@GuardedBy("mLock") 163private void startAnimationLocked(RunningAnimation a) { //step1.建立一個valueanimator 164final ValueAnimator anim = mAnimatorFactory.makeAnimator(); 165//step2.根據AnimationSpec設定這個VA的屬性以及註冊回撥函式 166// Animation length is already expected to be scaled. 167anim.overrideDurationScale(1.0f); 168anim.setDuration(a.mAnimSpec.getDuration()); 169anim.addUpdateListener(animation -> {//這個是動畫更新的回撥 170synchronized (mCancelLock) { 171if (!a.mCancelled) { 172final long duration = anim.getDuration(); 173long currentPlayTime = anim.getCurrentPlayTime(); 174if (currentPlayTime > duration) { 175currentPlayTime = duration; 176} 177applyTransformation(a, mFrameTransaction, currentPlayTime);//根據當前的時間進行更新變換 178} 179} 180 181// Transaction will be applied in the commit phase. 182scheduleApplyTransaction();//在下一次Vsync訊號到來的時候使前面的修改生效 183}); 184 185anim.addListener(new AnimatorListenerAdapter() { 186@Override 187public void onAnimationStart(Animator animation) { 188synchronized (mCancelLock) { 189if (!a.mCancelled) { 190mFrameTransaction.show(a.mLeash);//動畫開始的時候顯示leash這個Surface 191} 192} 193} 194 195@Override 196public void onAnimationEnd(Animator animation) { 197synchronized (mLock) { 198mRunningAnimations.remove(a.mLeash); 199synchronized (mCancelLock) { 200if (!a.mCancelled) { 201 202// Post on other thread that we can push final state without jank. 203AnimationThread.getHandler().post(a.mFinishCallback); 204} 205} 206} 207} 208}); 209a.mAnim = anim; //step3.加入到正在執行的動畫map中 210mRunningAnimations.put(a.mLeash, a); 211 //step4.呼叫VA的start,動畫的執行是依賴於VA本身的執行框架(ValueAnimation) 212anim.start(); 213if (a.mAnimSpec.canSkipFirstFrame()) { 214// If we can skip the first frame, we start one frame later. 215anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS); 216} 217 218// Immediately start the animation by manually applying an animation frame. Otherwise, the 219// start time would only be set in the next frame, leading to a delay. 220anim.doAnimationFrame(mChoreographer.getFrameTime()); 221}
step1.這裡建立的是一個ValueAnimator來執行動畫
step2.設定ValueAnimator的回撥函式
step3.將該動畫加到一個正在執行的animation map中
step4.呼叫start開始動畫
這裡VA的startAnimation中就有非同步trace,也就是開始時P中執行動畫的"animator"這個非同步trace(這裡的getNameForTrace返回的就是"animator")
1243/** 1244* Called internally to start an animation by adding it to the active animations list. Must be 1245* called on the UI thread. 1246*/ 1247private void startAnimation() { 1248if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { //非同步trace開始 1249Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), 1250System.identityHashCode(this)); 1251} 1252 1253mAnimationEndRequested = false; 1254initAnimation(); 1255mRunning = true; 1256if (mSeekFraction >= 0) { 1257mOverallFraction = mSeekFraction; 1258} else { 1259mOverallFraction = 0f; 1260} 1261if (mListeners != null) { 1262notifyStartListeners(); 1263} 1264}
之後的VA執行動畫的原理就不往下看了,再回到是如何發起動畫的,這裡也是存在差異的
這裡的無鎖動畫主要是針對應用切換的過場動畫 (其實新的動畫框架還支援APP修改視窗切換動畫,最近任務動畫,但都是依賴於這個無鎖動畫的框架)
在AppWindowToken的applayAnimationLocked中
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, 1723boolean isVoiceInteraction) { 1724 ...... 1733 1734// Only apply an animation if the display isn't frozen. If it is frozen, there is no reason 1735// to animate and it can cause strange artifacts when we unfreeze the display if some 1736// different animation is running. 1737Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked"); 1738if (okToAnimate()) { 1739final AnimationAdapter adapter; 1740final TaskStack stack = getStack(); 1741mTmpPoint.set(0, 0); 1742mTmpRect.setEmpty(); 1743if (stack != null) { 1744stack.getRelativePosition(mTmpPoint); 1745stack.getBounds(mTmpRect); 1746mTmpRect.offsetTo(0, 0); 1747} 1748 1749// Delaying animation start isn't compatible with remote animations at all. 1750if (mService.mAppTransition.getRemoteAnimationController() != null 1751&& !mSurfaceAnimator.isAnimationStartDelayed()) { 1752adapter = mService.mAppTransition.getRemoteAnimationController() 1753.createAnimationAdapter(this, mTmpPoint, mTmpRect); 1754} else { 1755final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); 1756if (a != null) { //先構造AnimationAdapter 1757adapter = new LocalAnimationAdapter( 1758new WindowAnimationSpec(a, mTmpPoint, mTmpRect, 1759mService.mAppTransition.canSkipFirstFrame(), 1760mService.mAppTransition.getAppStackClipMode(), 1761true /* isAppAnimation */), 1762mService.mSurfaceAnimationRunner); 1763if (a.getZAdjustment() == Animation.ZORDER_TOP) { 1764mNeedsZBoost = true; 1765} 1766mTransit = transit; 1767mTransitFlags = mService.mAppTransition.getTransitFlags(); 1768} else { 1769adapter = null; 1770} 1771} 1772if (adapter != null) { //呼叫WC的startAnimation啟動動畫,就回到了前面的分析中了 1773startAnimation(getPendingTransaction(), adapter, !isVisible()); 1774if (adapter.getShowWallpaper()) { 1775mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; 1776} 1777}
對比O中的相同函式實現在WindowManagerService中
2357boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, 2358int transit, boolean enter, boolean isVoiceInteraction) { 2359// Only apply an animation if the display isn't frozen.If it is 2360// frozen, there is no reason to animate and it can cause strange 2361// artifacts when we unfreeze the display if some different animation 2362// is running. 2363Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked"); ...... 2402if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition." 2403+ " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter 2404+ " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets); 2405final Configuration displayConfig = displayContent.getConfiguration(); 2406Animation a = mAppTransition.loadAnimation(lp, transit, enter, displayConfig.uiMode, 2407displayConfig.orientation, frame, displayFrame, insets, surfaceInsets, 2408stableInsets, isVoiceInteraction, freeform, atoken.getTask().mTaskId); 2409if (a != null) { 2410if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken); 2411final int containingWidth = frame.width(); 2412final int containingHeight = frame.height(); //這裡是給AppWindowToken對應的AppWindowAnimator設定好Animation 2413atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight, width, 2414height, mAppTransition.canSkipFirstFrame(), 2415mAppTransition.getAppStackClipMode(), 2416transit, mAppTransition.getTransitFlags()); 2417} 2418} else {
O中的實現是給需要執行動畫的appWindowToken設定好動畫,等到WindowAniamtor執行animate的時候去遍歷每一個有設定動畫的視窗即可,所以之前的動畫遇到其他執行緒持鎖是會變卡的
總結起來就是將視窗的動畫從WindowAnimator的animate函式中剝離出來,讓APP的切換動畫不受鎖的限制,避免了卡頓的風險。