基于AndroidR源碼分析
Android WMS動畫系統(tǒng)初探(一)
Android WMS動畫系統(tǒng)初探(二)
Android WMS動畫系統(tǒng)初探(三)
Android 動畫原理
Android中動畫的工作過程:在某一個時間點敢会,調用getTransformation()倘零,根據mStartTime和mDuration,計算出當前的進度钥顽,在根據mInterpolator計算出轉換的進度腮考,然后計算出屬性的當前值诚欠,保存在matrix中使碾。
再調用Matrix.getValues將屬性值取出底洗,運用在動畫目標上款筑。
Animation 和 Transform
[圖片上傳失敗...(image-8c5ae5-1636101404926)]
Animation
在給定了初始狀態(tài)智蝠、結束狀態(tài)、啟動時間與持續(xù)時間后奈梳,可以為使用者計算其動畫目標在任意時刻的變換(Transformation)
子類:TranslateAnimation杈湾,ScaleAnimation,RotateAnimation颈嚼,AlphaAnimation
Transformation
描述了一個變換毛秘,包含兩個分量:透明度和一個二維變換矩陣
Choreographer
無論APP或者系統(tǒng),都是可以直接向Choreographer注冊FrameCallback來實現(xiàn)動畫驅動的阻课。
Choreographer 類似 Handler叫挟,處理回調的時機為屏幕的垂直同步(VSync)事件到來之時,其處理回調的過程被當作渲染下一幀的工作的一部分
postCallback(int callbackType, Runnable action, Object token)
在下一次 VSync 時執(zhí)行 action 所指定的操作限煞。
callbackType 的取值:
CALLBACK_INPUT:處理輸入事件
CALLBACK_ANIMATION:處理動畫事
CALLBACK_TRAVERSAL:處理布局
postCallbackDelayed(int callbackType, Runnable action, Object token, delayMillis)
比 postCallback 增加了一個延遲
postFrameCallback(FrameCallback callback)
在下一次 VSync 時執(zhí)行 callback 指定的回調抹恳。與 postCallback 本質沒有太大區(qū)別,其回調類型強制為 CALLBACK_ANIMATION署驻。FrameCallback 接口的定義函數(shù)為:doFrame(long frameTimeNanos)奋献,參數(shù)是各納秒級的時間戳這個函數(shù)是為處理動畫幀所涉及的postFrameCallbackDelayed(FrameCallback callback, int timeDelayed)比postFrameCallback 增加了一個延遲
WMS的動畫系統(tǒng)
窗口動畫的本質
對于View動畫,動畫的目標就是View旺上,而對于窗口來說瓶蚂,動畫的目標其實都是Surface,對不同層級的SurfaceControl進行操縱宣吱,會產生不同的動畫效果窃这。
目標WindowContainer | 名稱 | 舉例 |
---|---|---|
WindowState | 窗口動畫 | Toast的彈出動畫、PopupWindow的彈出動畫 |
AppWindowToken | 過渡動畫 | App從桌面啟動的動畫 |
Task | Task動畫 | Recents的動畫征候,PIP動畫 |
DisplayContent | 全屏動畫 | 轉屏動畫 |
WMS類結構
[圖片上傳失敗...(image-b25ef9-1636101404926)]
WMS結構層次
如上圖 WMS的結構層次可以簡單概括為:
RootWindowContainer -> DisplayContent -> DisplayArea -> Task -> WindowToken -> WindowState
[圖片上傳失敗...(image-2bdd8a-1636101404926)]
根據操縱層級的不同我把動畫分類為:窗口動畫杭攻、過渡動畫、Task動畫疤坝、全屏動畫等等
窗口動畫
窗口動畫的啟動入口
在DisplayContent的applySurfaceChangesTransaction函數(shù)中兆解,會調用每個窗口的WindowStateAnimator#commitFinishDrawingLocked,這個函數(shù)是用于處理繪制狀態(tài)為COMMIT_DRAW_PENDING或READY_TO_SHOW的窗口跑揉,因為窗口到了這兩個狀態(tài)才能做窗口動畫锅睛。
隨后會調用WindowState#performShowLocked,并調用WSA的applyEnterAnimationLocked,最后把繪制狀態(tài)改為HAS_DRAWN衣撬。
當進入WSA的applyEnterAnimationLocked之后乖订,后面走的是Surface動畫的統(tǒng)一流程,這個我們在后面統(tǒng)一講。當窗口的狀態(tài)變成HAS_DRAW后具练,會在prepareSurface中被show出來,這樣子窗口已經變?yōu)榭梢娞鹞蓿㈤_始做動畫.
DisplayContent#applySurfaceChangesTransaction
void applySurfaceChangesTransaction() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
try {
// 這里會調用WindowState#performShowLocked
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// 這里的流程最終會調用mSurfaceControl.show()真正顯示出surface
prepareSurfaces();
}
[圖片上傳失敗...(image-d5ed20-1636101404926)]
在窗口布局(relayout)階段調用到
WindowStateAnimator#commitFinishDrawingLocked ->
WindowState#performShowLocked ->
WindowStateAnimator#applyEnterAnimationLocked
開啟窗口動畫流程
WindowStateAnimator#applyAnimationLocked
boolean applyAnimationLocked(int transit, boolean isEntrance) {
if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
return true;
}
// 設置輸入法相關動畫
final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD;
if (isEntrance && isImeWindow) {
mWin.getDisplayContent().adjustForImeIfNeeded();
mWin.setDisplayLayoutNeeded();
mService.mWindowPlacerLocked.requestTraversal();
}
// 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.
if (mWin.mToken.okToAnimate()) {
// 通過DisplayPolicy選擇StatusBar或NavigationBar的動畫
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
if (anim != DisplayPolicy.ANIMATION_NONE) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
// 加載動畫
a = AnimationUtils.loadAnimation(mContext, anim);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
} 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 = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
mWin.mAttrs, attr, TRANSIT_NONE);
}
}
...
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
// 流程轉到WindowState#startAnimation 執(zhí)行動畫
mWin.startAnimation(a);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mAnimationIsEntrance = isEntrance;
}
} else if (!isImeWindow) {
mWin.cancelAnimation();
}
if (!isEntrance && isImeWindow) {
mWin.getDisplayContent().adjustForImeIfNeeded();
}
return mWin.isAnimating(PARENTS);
}
WindowState#startAnimation
void startAnimation(Animation anim) {
// If we are an inset provider, all our animations are driven by the inset client.
if (mControllableInsetProvider != null) {
return;
}
final DisplayInfo displayInfo = getDisplayInfo();
// 重置Animation扛点,并設置mInitialized為true
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
// 設置動畫最長時間,默認10s
anim.restrictDuration(MAX_ANIMATION_DURATION);
// 設置動畫scale
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
// 構建LocalAnimationAdapter岂丘,封裝了WindowAnimationSpec和SurfaceAnimationRunner
// WindowAnimationSpec中封裝了animation陵究、surface位置、stackBounds等信息
// SurfaceAnimationRunner創(chuàng)建于WMS構建之時奥帘,
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
// mSurfaceAnimator.startAnimation
startAnimation(getPendingTransaction(), adapter);
// 再次調用WMS.scheduleAnimationLocked()
commitPendingTransaction();
}
SurfaceAnimationRunner
// com/android/server/wm/WindowManagerService.java
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
...
mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,
mPowerManagerInternal);
...
}
// com/android/server/wm/SurfaceAnimationRunner.java
SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
AnimatorFactory animatorFactory, Transaction frameTransaction,
PowerManagerInternal powerManagerInternal) {
// 從ThreadLocal取出SF的Choreographer
mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(),
0 /* timeout */);
mFrameTransaction = frameTransaction;
mAnimationHandler = new AnimationHandler();
mAnimationHandler.setProvider(callbackProvider != null
? callbackProvider
: new SfVsyncFrameCallbackProvider(mChoreographer));
// factory用于創(chuàng)建SfValueAnimator
mAnimatorFactory = animatorFactory != null
? animatorFactory
: SfValueAnimator::new;
mPowerManagerInternal = powerManagerInternal;
}
private class SfValueAnimator extends ValueAnimator {
SfValueAnimator() {
setFloatValues(0f, 1f);
}
@Override
public AnimationHandler getAnimationHandler() {
return mAnimationHandler;
}
}
什么是Leash
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java中定義了一個mSurfaceAnimator成員變量
SurfaceAnimator的startAnimation方法中創(chuàng)建Leash铜邮,可以通過SurfaceAnimator的類注釋了解Leash
/**
* 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的對象進行動畫,在執(zhí)行動畫的過程中會創(chuàng)建一個沒有Buffer的Surface---“Leash”寨蹋,將所有child surface綁定到leash上松蒜,leash同時也會綁定到原先這些child surface綁定的位置。然后我們將leash給到AnimationAdapter去執(zhí)行動畫已旧,執(zhí)行動畫結束后會將所有child surface重新綁定到原先的父節(jié)點上秸苗。
為什么引入Leash可以參考此文:Android P——LockFreeAnimation
SurfaceAnimator#startAnimation
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
mAnimationFinishedCallback = animationFinishedCallback;
// step1 : 先獲取當前需要執(zhí)行動畫的surface
final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
cancelAnimation();
return;
}
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
if (mLeash == null) {
// step2 : 用step1的surface創(chuàng)建一個leash
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden, mService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
}
// step3 : 將leash傳給AnimationAdapter,執(zhí)行動畫
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
}
- 先獲取當前需要執(zhí)行動畫的surface
- 用step1的surface創(chuàng)建一個leash,這個流程看樣子會遞歸調用到根節(jié)點到DisplayContent中运褪,這里不做深入
- 將leash傳給AnimationAdapter惊楼,執(zhí)行動畫
mAnimation.startAnimation這一步最終會通過LocalAnimationAdapter找到WMS里的SurfaceAnimationRunner進行執(zhí)行。
這是 WindowContainer與SurfaceAnimtor秸讹、SurfaceAnimationRunner的持有關系 :
[圖片上傳失敗...(image-f741c0-1636101404926)]
SurfaceAnimationRunner#startAnimation
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
// 封裝RunningAnimation對象
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
// 加入mPendingAnimations這個ArrayMap
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred) {
// 等待下一次Vsync執(zhí)行startAnimations()檀咙,開始執(zhí)行動畫
mChoreographer.postFrameCallback(this::startAnimations);
}
// 一些動畫(例如移動動畫)需要立即應用初始變換。
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
往編舞者上拋的runnable是執(zhí)行startAnimations方法
SurfaceAnimationRunner#startAnimations ->
SurfaceAnimationRunner#startPendingAnimationsLocked
會從mPendingAnimations遍歷RunningAnimation并執(zhí)行startAnimationLocked
SurfaceAnimationRunner#startAnimationLocked
@GuardedBy("mLock")
private void startAnimationLocked(RunningAnimation a) {
// 使用AnimationFactory創(chuàng)建一個SfValueAnimator
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
anim.overrideDurationScale(1.0f);
anim.setDuration(a.mAnimSpec.getDuration());
// 實現(xiàn)UpdaterListener處理每一幀動畫
anim.addUpdateListener(animation -> {
synchronized (mCancelLock) {
if (!a.mCancelled) {
final long duration = anim.getDuration();
long currentPlayTime = anim.getCurrentPlayTime();
if (currentPlayTime > duration) {
currentPlayTime = duration;
}
// 計算Transformation應用到leash中
// 實際執(zhí)行的是前面封裝的WindowAnimationSpec#apply方法
// 這里會計算真正要執(zhí)行的的動畫(Transformation)效果
// 這一步的目標是為mFrameTransaction設置要執(zhí)行的事務
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// Transaction will be applied in the commit phase.
// 在下一個Vsync信號到來時璃诀,提交動畫事務(mFrameTransaction)
scheduleApplyTransaction();
});
// 設置動畫開始和完成時的處理
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
synchronized (mCancelLock) {
if (!a.mCancelled) {
// TODO: change this back to use show instead of alpha when b/138459974 is
// fixed.
mFrameTransaction.setAlpha(a.mLeash, 1);
}
}
}
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
mRunningAnimations.remove(a.mLeash);
synchronized (mCancelLock) {
if (!a.mCancelled) {
// Post on other thread that we can push final state without jank.
mAnimationThreadHandler.post(a.mFinishCallback);
}
}
}
}
});
a.mAnim = anim;
// 動畫啟動前將這個ValueAnimator加入mRunningAnimations這個ArrayMap
mRunningAnimations.put(a.mLeash, a);
// 真正開啟動畫
anim.start();
if (a.mAnimSpec.canSkipFirstFrame()) {
// If we can skip the first frame, we start one frame later.
anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
}
// 通過手動應用動畫框架立即啟動動畫弧可。 否則,開始時間只會在下一個幀中設置文虏,導致延遲侣诺。
anim.doAnimationFrame(mChoreographer.getFrameTime());
}
這一步構建了一個SfValueAnimator來真正的驅動動畫,每一幀的處理是通過WindowAnimationSpec構建真正要執(zhí)行的動畫事務氧秘,然后使用mChoreographer.postCallback在下一個vsync信號到來時提交動畫事務年鸳。
ValueAnimator驅動動畫的原理本文就不做深入了。
下一篇文章我將進一步分析Activiy的過渡動畫和屏幕旋轉動畫的相關流程丸相。