序
如何创建一个窗口动画?我们通过先从APP创建一个窗口,以这个窗口的创建过程的窗口动画为例
这个demo就是点击BUTTON显示窗口,点击CLOSE WINDOW关闭窗口,下面简述关键代码
//定义WindowManager和LayoutParams
private WindowManager mWindowManager;
private WindowManager.LayoutParams mLayoutParams;
//取得系统窗体
mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
//窗体的布局样式
mLayoutParams = new WindowManager.LayoutParams();
//窗口设置动画
mLayoutParams.windowAnimations = R.style.MyWindow;
//设置窗口名字
mLayoutParams.setTitle("test-window");
在res/values/styles.xml目录下添加styles
<style name="MyWindow">
<item name="android:windowEnterAnimation">@anim/enter</item>
<item name="android:windowExitAnimation">@anim/exit</item>
</style>
android:windowEnterAnimation
和android:windowExitAnimation
属性指定进入和退出时的转场动画。
创建res/anim/enter.xml和res/anim/exit.xml
enter.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0"
android:toAlpha="1.0"
android:duration="1000"/>
</set>
设置透明度从0到1
exit.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0"
android:toAlpha="0"
android:duration="1000"/>
</set>
设置透明度从1到0
这两个xml都只是简单的做了一个透明度变化,实现了一个淡入淡出的效果
注:该demo可见附件,或者窗口相关的演示demo
分析思路
我们如何知道上面的demo所涉及的动画在我们framework侧的哪个部分?
图层
根据上面的demo我们从winscope上看看在图层上是怎么显示的
我们点击BUTTON后添加窗口,从图层中可以看到WindowToken和我们创建的窗口test-window中间添加了一个动画Surface(name=2257b8c test-window)/@0x825faea - animation-leash of window_animation#529
。同时我们也能看到test-window的color : r:-1.000 g:-1.000 b:-1.000 a:0.193603515625
,其中a:0.193603515625表示透明度,透明度从0逐渐到1就是从透明到显示的过程。
透明度为1时动画退出,窗口完全显示;窗口的移除流程同理,唯一不同的就是透明度从1到0,透明度为0时动画移除,窗口完全退出。
其过程我们简化如下图所示:
上图就是一次完整动画图层显示过程,也就是说动画的显示过程就是为其添加或移除的窗口和这个该窗口的父窗口之前添加一个层级(leash)用于显示动画;动画播放完成后,再移除这个层级(leash)。
注:Surface(name=2257b8c test-window)/@0x825faea - animation-leash of window_animation#529
中的window_animation
表示动画的类型是窗口动画。还有一种动画类型是insets_animation
(一般是输入法、状态栏、导航栏等涉及),流程上大体也相同,这里我们不讨论
代码
添加动画
从上面的例子来看,不论是窗口显示还是隐藏,都会有类似于Surface(name=2257b8c test-window)/@0x825faea - animation-leash of window_animation#529
的动画,那么我们就可以通过这点切入查找相应的代码,dump信息在代码中基本都有迹可循,搜索animation-leash
找到对应代码位置frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
我们可以在createAnimationLeash方法中添加堆栈来查看其调用流程
Slog.i("WindowManager:","createAnimationLeash type = " + animationTypeToString(type) , new Exception());
也可以使用走读代码的方式追踪
移除动画
既然有createAnimationLeash,那么就会有类似removeAnimationLeash\removeLeash\removeAnimation相关方法,我们逐个排查
找到removeLeash方法,同样在frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java中
我们可以在removeLeash方法中添加堆栈来查看其调用流程
Slog.i("WindowManager:","removeLeash leash = " + leash , new Exception());
同样也可以使用走读代码的方式追踪
堆栈
动画添加
简易图:
先来看看Demo中点击Button显示窗口和点击CLOSE_WINDOW退出窗口的动画的显示流程
1.点击Button添加窗口的动画流程
WindowManager: createAnimationLeash type = window_animation
WindowManager: java.lang.Exception
WindowManager: at com.android.server.wm.SurfaceAnimator.createAnimationLeash(SurfaceAnimator.java:458)
WindowManager: at com.android.server.wm.SurfaceAnimator.startAnimation(SurfaceAnimator.java:184)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2757)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2764)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2770)
WindowManager: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5305)
WindowManager: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5281)
WindowManager: at com.android.server.wm.WindowStateAnimator.applyAnimationLocked(WindowStateAnimator.java:655)
WindowManager: at com.android.server.wm.WindowStateAnimator.applyEnterAnimationLocked(WindowStateAnimator.java:583)
WindowManager: at com.android.server.wm.WindowState.performShowLocked(WindowState.java:4648)
WindowManager: at com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked(WindowStateAnimator.java:276)
WindowManager: at com.android.server.wm.DisplayContent.lambda$new$8$com-android-server-wm-DisplayContent(DisplayContent.java:987)
WindowManager: at com.android.server.wm.DisplayContent$$ExternalSyntheticLambda14.accept(Unknown Source:4)
WindowManager: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2629)
WindowManager: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2619)
WindowManager: at com.android.server.wm.WindowState.applyInOrderWithImeWindows(WindowState.java:4904)
WindowManager: at com.android.server.wm.WindowState.forAllWindows(WindowState.java:4748)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1616)
WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1633)
WindowManager: at com.android.server.wm.DisplayContent.applySurfaceChangesTransaction(DisplayContent.java:4666)
WindowManager: at com.android.server.wm.RootWindowContainer.applySurfaceChangesTransaction(RootWindowContainer.java:1021)
WindowManager: at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:824)
WindowManager: at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:784)
WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:177)
WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:115)
WindowManager: at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:57)
WindowManager: at android.os.Handler.handleCallback(Handler.java:942)
WindowManager: at android.os.Handler.dispatchMessage(Handler.java:99)
WindowManager: at android.os.Looper.loopOnce(Looper.java:201)
WindowManager: at android.os.Looper.loop(Looper.java:288)
WindowManager: at android.os.HandlerThread.run(HandlerThread.java:67)
WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:44)
2.点击CLOSE_WINDOW移除窗口的动画流程
WindowManager: createAnimationLeash type = window_animation
WindowManager: java.lang.Exception
WindowManager: at com.android.server.wm.SurfaceAnimator.createAnimationLeash(SurfaceAnimator.java:458)
WindowManager: at com.android.server.wm.SurfaceAnimator.startAnimation(SurfaceAnimator.java:184)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2757)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2764)
WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2770)
WindowManager: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5305)
WindowManager: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5281)
WindowManager: at com.android.server.wm.WindowStateAnimator.applyAnimationLocked(WindowStateAnimator.java:655)
WindowManager: at com.android.server.wm.WindowState.removeIfPossible(WindowState.java:2600)
WindowManager: at com.android.server.wm.WindowState.removeIfPossible(WindowState.java:2498)
WindowManager: at com.android.server.wm.WindowManagerService.removeWindow(WindowManagerService.java:2033)
WindowManager: at com.android.server.wm.Session.remove(Session.java:223)
WindowManager: at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:684)
WindowManager: at com.android.server.wm.Session.onTransact(Session.java:175)
WindowManager: at android.os.Binder.execTransactInternal(Binder.java:1285)
WindowManager: at android.os.Binder.execTransact(Binder.java:1244)
3.添加窗口和移除窗口的动画显示流程差异
从上述堆栈中我们可以发现,无论是窗口的添加还是移除,其动画都会调用WindowStateAnimator.applyAnimationLocked到SurfaceAnimator.createAnimationLeash创建动画
添加窗口走WindowState.performShowLocked
流程调用到WindowStateAnimator.applyAnimationLocked
移除窗口走WindowState.removeIfPossible
流程调用到WindowStateAnimator.applyAnimationLocked
特别的,添加窗口时,如果没有动画需要创建则会直接给一个空的动画资源加载;但是在移除窗口时,如果没有动画需要创建则不会走到WindowStateAnimator.applyAnimationLocked
,而是直接走到WindowState.removeImmediately
移除窗口
动画移除
简易图:
动画播放完成,去掉leash图层
WindowManager: removeLeash leash = Surface(name=2257b8c test-window)/@0x825faea
WindowManager: java.lang.Exception
WindowManager: at com.android.server.wm.SurfaceAnimator.removeLeash(SurfaceAnimator.java:417)
WindowManager: at com.android.server.wm.SurfaceAnimator.reset(SurfaceAnimator.java:410)
WindowManager: at com.android.server.wm.SurfaceAnimator.lambda$getFinishedCallback$0$com-android-server-wm-SurfaceAnimator(SurfaceAnimator.java:131)
WindowManager: at com.android.server.wm.SurfaceAnimator$$ExternalSyntheticLambda1.run(Unknown Source:8)
WindowManager: at com.android.server.wm.SurfaceAnimator.lambda$getFinishedCallback$1$com-android-server-wm-SurfaceAnimator(SurfaceAnimator.java:141)
WindowManager: at com.android.server.wm.SurfaceAnimator$$ExternalSyntheticLambda0.onAnimationFinished(Unknown Source:4)
WindowManager: at com.android.server.wm.LocalAnimationAdapter.lambda$startAnimation$0$com-android-server-wm-LocalAnimationAdapter(LocalAnimationAdapter.java:67)
WindowManager: at com.android.server.wm.LocalAnimationAdapter$$ExternalSyntheticLambda0.run(Unknown Source:6)
WindowManager: at android.os.Handler.handleCallback(Handler.java:942)
WindowManager: at android.os.Handler.dispatchMessage(Handler.java:99)
WindowManager: at android.os.Looper.loopOnce(Looper.java:201)
WindowManager: at android.os.Looper.loop(Looper.java:288)
WindowManager: at android.os.HandlerThread.run(HandlerThread.java:67)
WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:44)
动画添加流程
以添加窗口的动画为例,窗口添加的流程见Android T WMS窗口相关流程
这里我们从WindowStateAnimator.commitFinishDrawingLocked方法开始跟踪代码流程
修改窗口状态和判断窗口类型
WindowStateAnimator.commitFinishDrawingLocked()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked() {
//如果开启了DEBUG_STARTING_WINDOW_VERBOSE调试模式,并且窗口的类型是TYPE_APPLICATION_STARTING,则打印日志
if (DEBUG_STARTING_WINDOW_VERBOSE && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState=" + drawStateToString());
}
//如果绘制状态不是COMMIT_DRAW_PENDING和READY_TO_SHOW,则返回false
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s", mSurfaceController);
//设置绘制状态为READY_TO_SHOW
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
//根据条件进入performShowLocked()流程
if (activity == null || activity.canShowWindows() || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
这个方法主要就是做了两件事:
mDrawState = READY_TO_SHOW
将窗口的状态修改为READY_TO_SHOW- 根据条件进入到WindowState.performShowLocked()流程
activity == null
如果ActivityRecord为空,这种情况可以理解为不依赖Activity的窗口,比如常见的悬浮窗activity.canShowWindows()
这个方法大概是说:只有当所有窗口都已绘制完成,并且没有正在进行父级窗口的应用过渡动画,并且没有非默认颜色的窗口存在时,返回truemWin.mAttrs.type == TYPE_APPLICATION_STARTING
窗口类型为启动窗口,启动窗口就是StartingWindow,应用启动时出现的窗口,常见的就是Splash screen ,许多应用都会定义自己的SplashActivity
即使非这三种情况,最终也会调用到performShowLocked()
上述Demo仅仅只是通过addView添加窗口,因此ActivityRecord为空,属于activity == null
这种情况,我们这里直接走performShowLocked()
进入窗口动画流程
WindowState.performShowLocked()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
// This must be called while inside a transaction.
boolean performShowLocked() {
......
//获取WindowStateAnimator.mDrawState
final int drawState = mWinAnimator.mDrawState;
//这里判断(drawState 状态为HAS_DRAWN 或者READY_TO_SHOW)且ActivityRecord不为空
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
//窗口类型不为启动窗口
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}
}
//如果当前mDrawState的状态不为READY_TO_SHOW ,则直接返回
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
return false;
}
......
//走入窗口动画流程
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
//设置mDrawState的状态为HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
......
return true;
}
mWinAnimator.applyEnterAnimationLocked();
进入动画流程,之后mDrawState更新为HAS_DRAWN。
确定transit的值调用后续动画流程
WindowStateAnimator.applyEnterAnimationLocked()
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;
}
//1.根据mEnterAnimationPending变量确定transit的值
final int transit;
if (mEnterAnimationPending) {
mEnterAnimationPending = false;
transit = WindowManagerPolicy.TRANSIT_ENTER;
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
// We don't apply animation for application main window here since this window type
// should be controlled by ActivityRecord in general. Wallpaper is also excluded because
// WallpaperController should handle it.
//2.根据条件调用applyAnimationLocked()
if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
applyAnimationLocked(transit, true);
}
if (mService.mAccessibilityController.hasCallbacks()) {
mService.mAccessibilityController.onWindowTransition(mWin, transit);
}
}
- 根据mEnterAnimationPending变量确定transit的值。
如果这个变量为真,那么会执行一个进入动画,否则会执行一个显示动画。同时,这段代码还确保了transit变量始终被赋予一个有效值,即WindowManagerPolicy.TRANSIT_ENTER
或WindowManagerPolicy.TRANSIT_SHOW
。
在我们addWindow流程中mEnterAnimationPending会被赋值为true
因此,我们的transit = WindowManagerPolicy.TRANSIT_ENTER
,即transit = 1
transit一般有下面这几种类型:/** Window has been added to the screen. */ public static final int TRANSIT_ENTER = 1; /** Window has been removed from the screen. */ public static final int TRANSIT_EXIT = 2; /** Window has been made visible. */ public static final int TRANSIT_SHOW = 3; /** Window has been made invisible. * TODO: Consider removal as this is unused. */ public static final int TRANSIT_HIDE = 4; /** The "application starting" preview window is no longer needed, and will * animate away to show the real window. */ public static final int TRANSIT_PREVIEW_DONE = 5;
- 根据条件调用applyAnimationLocked()
mAttrType != TYPE_BASE_APPLICATION
窗口类型不是Activity!mIsWallpape
当前窗口不是壁纸
大概就是说,Activity类型的窗口一般由ActivityRecord控制,壁纸类型由WallpaperController控制。我们这里窗口类型既不是Activity也不是壁纸,所以调用applyAnimationLocked(transit, true);
综上所述,调用的是applyAnimationLocked(1, true);
加载并启动动画
WindowStateAnimator.applyAnimationLocked()
/**
* Choose the correct animation and set it to the passed WindowState.
* @param transit If AppTransition.TRANSIT_PREVIEW_DONE and the app window has been drawn
* then the animation will be app_starting_exit. Any other value loads the animation from
* the switch statement below.
* @param isEntrance The animation type the last time this was called. Used to keep from
* loading the same animation twice.
* @return true if an animation has been loaded.
*/
boolean applyAnimationLocked(int transit, boolean isEntrance) {
//mWin.isAnimating(),检查窗口是否已经在执行动画,
//mAnimationIsEntrance == 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();
//让WMS知道窗口布局已经改变,需要重新计算布局
mWin.setDisplayLayoutNeeded();
//请求WMS进行一次遍历,以重新计算和更新窗口布局
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.
//mWin.mToken.okToAnimate()会判断是否有冻结,屏幕是否开启
if (mWin.mToken.okToAnimate()) {
//selectAnimation(mWin, transit)根据mWin(当前窗口)和transit的值来选择显示的动画,之后返回给anim变量
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
//根据anim的值选择动画加载方式
if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
//anim的值不为ANIMATION_NONE(-1)
if (anim != DisplayPolicy.ANIMATION_NONE) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
//loadAnimation方法加载与anim对应的动画,并将加载的动画赋值给变量a
a = AnimationUtils.loadAnimation(mContext, anim);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
} else {
//根据transit的值来给attr赋值,我们这里transit是WindowManagerPolicy.TRANSIT_ENTER
switch (transit) {
case WindowManagerPolicy.TRANSIT_ENTER:
//attr=0x0
attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
break;
case WindowManagerPolicy.TRANSIT_EXIT:
//attr=0x1
attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
break;
case WindowManagerPolicy.TRANSIT_SHOW:
//attr=0x2
attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
break;
case WindowManagerPolicy.TRANSIT_HIDE:
//attr=0x3
attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
break;
}
if (attr >= 0) {
//loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_OLD_NONE)用来加载动画属性。
a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
//log打印,打开开关adb shell wm logging enable-text WM_DEBUG_ANIM
if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s"
+ " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20));
}
if (a != null) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
//启动动画
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(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
其中根据条件if (anim != DisplayPolicy.ANIMATION_STYLEABLE)
为true还是false,会判断后续动画是通过AnimationUtils.loadAnimation加载,还是通过AppTransition.loadAnimationAttr加载。
无论哪种方式加载动画,最终都会通过mWin.startAnimation(a);
启动动画。
下面我们从上往下来讲讲这段代码中有一些关键部分。
根据窗口类型获取anim的返回值
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
根据mWin(当前窗口)和transit的值来选择显示的动画,之后返回给anim变量
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
int selectAnimation(WindowState win, int transit) {
ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
if (win == mStatusBar) {
......
} else if (win == mNavigationBar) {
......
} else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt
|| win == mExtraNavBarAlt) {
......
}
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
if (win.isActivityTypeHome()) {
// Dismiss the starting window as soon as possible to avoid the crossfade out
// with old content because home is easier to have different UI states.
return ANIMATION_NONE;
}
ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
return R.anim.app_starting_exit;
}
}
return ANIMATION_STYLEABLE;
}
win == mStatusBar
、win == mNavigationBar
判断我们的窗口是否是状态栏、导航栏
win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt || win == mExtraNavBarAlt
其他的一些特定窗口,这些特定的窗口对象可能是代表不同的屏幕区域或组件,例如备用的状态栏、备用的导航栏、气候栏和额外的导航栏。
transit == TRANSIT_PREVIEW_DONE
应用启动预览窗口是否已经完成,即transit的值为5。
win.hasAppShownWindows()
当前窗口的应用是否已经显示过任何窗口。
win.isActivityTypeHome()
当前窗口是主页。
如果窗口预览动画已经完成的状态为true,当前窗口的应用已经显示过至少一个窗口,并且当前窗口是主页,三个条件都满足时,那么不使用任何动画,直接返回ANIMATION_NONE
。
也就是说selectAnimation方法实际上主要就是判断两件事:
1.当前窗口是否属于特定窗口(状态栏、导航栏等)
2.transit的值是否为TRANSIT_PREVIEW_DONE
,且当前窗口的应用已经显示过至少一个窗口(这个条件下满足当前窗口是主页时,返回ANIMATION_NONE
)
不是上述情况就返回ANIMATION_STYLEABLE
。
结合WindowStateAnimator.applyAnimationLocked代码中的逻辑来看,根据selectAnimation方法的返回值来确定调用哪个方法加载动画:
selectAnimation返回值为ANIMATION_NONE
,则不加载动画。
selectAnimation返回值为ANIMATION_STYLEABLE
,则通过AppTransition.loadAnimationAttr加载动画。
其他返回值,则通过AnimationUtils.loadAnimation加载动画。
我们这里既不为特定窗口,且transit的值不为5,因此直接返回ANIMATION_STYLEABLE
所以我们当前窗口动画走的是AppTransition.loadAnimationAttr流程加载动画。
特定窗口的动画加载方式
AnimationUtils.loadAnimation主要用于加载XML文件中定义的常规动画,这些动画可以用于任何View对象。
a = AnimationUtils.loadAnimation(mContext, anim);
从Android资源中加载一个动画,a为null则表示没有动画。
代码路径:frameworks/base/core/java/android/view/animation/AnimationUtils.java
/**
* Loads an {@link Animation} object from a resource
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animation object referenced by the specified id
* @throws NotFoundException when the animation cannot be loaded
*/
public static Animation loadAnimation(Context context, @AnimRes int id)
throws NotFoundException {
XmlResourceParser parser = null;
try {
//从给定的资源ID中获取一个动画资源。
parser = context.getResources().getAnimation(id);
//从解析器中创建一个Animation对象
return createAnimationFromXml(context, parser);
} catch (XmlPullParserException ex) {
NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} finally {
//解析器(parser)不为null,则关闭它。确保资源适当地释放
if (parser != null) parser.close();
}
}
这个方法的关键就是通过context.getResources().getAnimation(id)
,从给定的资源ID中获取一个动画资源。该方法返回一个XmlResourceParser对象,用于解析XML格式的动画资源。
再调用createAnimationFromXml(context, parser)
从解析器中创建一个Animation对象
private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
}
@UnsupportedAppUsage
private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
Animation anim = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
//使用一个循环来读取XML的各个节点。循环将继续,直到遇到一个结束标签或文档结束。
//如果当前节点不是起始标签,则继续下一次循环。
//如果是起始标签,检查标签的名称以确定要创建的动画类型。
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
//如果标签是"set",则创建一个新的AnimationSet对象,
//并递归地调用createAnimationFromXml函数来解析其子动画。
//如果标签是"alpha"、"scale"、"rotate"、"translate"、"cliprect"或"extend",
//则根据标签的名称创建相应的动画对象。
if (name.equals("set")) {
anim = new AnimationSet(c, attrs);
createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
} else if (name.equals("alpha")) {
anim = new AlphaAnimation(c, attrs);
} else if (name.equals("scale")) {
anim = new ScaleAnimation(c, attrs);
} else if (name.equals("rotate")) {
anim = new RotateAnimation(c, attrs);
} else if (name.equals("translate")) {
anim = new TranslateAnimation(c, attrs);
} else if (name.equals("cliprect")) {
anim = new ClipRectAnimation(c, attrs);
} else if (name.equals("extend")) {
anim = new ExtendAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}
if (parent != null) {
parent.addAnimation(anim);
}
}
//返回创建的动画
return anim;
}
这个方法就是根据应用侧定义XML,进行解析并创建动画。
最常见的应用侧调用补间动画的方式就是:
通过AnimationUtils.loadAnimation() 加载动画,然后我们的View控件调用startAnimation开启动画。
比如,先在res/anim目录下创建了一个exit.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0"
android:toAlpha="0"
android:duration="1000"/>
</set>
可以通过loadAnimation加载XML
ImageView img_show = (ImageView) findViewById(R.id.img_show);
//加载前面定义的exit.xml
Animation animation = AnimationUtils.loadAnimation(this, R.anim.exit);
img_show.startAnimation(animation);
注:img_show为某个ImageView控件ID
动画资源ID attr
attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation
对应的是代码路径:frameworks/base/core/res/res/values/attrs.xml
这也对应了我们应用侧代码中定义的style
<style name="MyWindow">
<item name="android:windowEnterAnimation">@anim/enter</item>
<item name="android:windowExitAnimation">@anim/exit</item>
</style>
attr
是资源的一个ID,一般来说,添加窗口时的动画通过android:windowEnterAnimation
属性设置时,attr
的值为0x0
;移除窗口时的动画通过android:windowExitAnimation
属性设置时,attr
的值为0x1
。attr
的值只与transit
有关,后面调用loadAnimationAttr方法时会用到。
非特定窗口的动画加载方式
AppTransition.loadAnimationAttr主要用于加载应用级别的转场动画。
a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_OLD_NONE);
mAppTransition是AppTransition对象,这个类是用于管理应用程序过渡动画的类,a为null则表示没有动画。
mWin.getDisplayContent().mAppTransition
获取一个窗口的显示内容中的应用程序过渡动画。
loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_OLD_NONE);
它传递了三个参数:mWin.mAttrs(对应我们应用侧定义的窗口属性),attr(我们之前看到它必须大于或等于0),以及TRANSIT_OLD_NONE(一个标志或状态,表示没有旧的过渡动画)
代码路径:frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
//LayoutParams lp 通常用于定义视图的位置和大小等属性,对应我们应用侧定义的LayoutParams对象;
//int animAttr 是动画属性,表示动画的类型或ID;
//int transit 表示过渡效果。
Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit);
}
mTransitionAnimation是TransitionAnimation对象,通过AppTransition调用到TransitionAnimation的loadAnimationAttr方法
代码路径:frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java
/** Load animation by attribute Id from specific LayoutParams */
@Nullable
public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
//定义了一个整数变量 resId 并初始化为 Resources.ID_NULL,用来存储动画资源的ID
int resId = Resources.ID_NULL;
Context context = mContext;
//如果 animAttr 大于等于0,则调用 getCachedAnimations(lp) 方法获取动画缓存,并将返回的动画资源的ID存储到 resId 中
if (animAttr >= 0) {
AttributeCache.Entry ent = getCachedAnimations(lp);
if (ent != null) {
context = ent.context;
resId = ent.array.getResourceId(animAttr, 0);
}
}
//调用 updateToTranslucentAnimIfNeeded(resId, transit) 方法,更新具有透明效果的动画资源 resId
resId = updateToTranslucentAnimIfNeeded(resId, transit);
//检查 resId 是否有效。如果有效,则调用 loadAnimationSafely(context, resId, mTag) 方法加载动画,并返回加载的动画对象;
//否则返回 null
if (ResourceId.isValid(resId)) {
return loadAnimationSafely(context, resId, mTag);
}
return null;
}
loadAnimationAttr 的方法,它主要功能是加载和动画相关的资源,这里我们主要看看getCachedAnimations(lp)
代码路径:frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
Slog.v(mTag, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
+ " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
}
//判断应用侧传递的参数 lp 是否为 null,以及 lp.windowAnimations 是否不为0,否则直接返回 null
if (lp != null && lp.windowAnimations != 0) {
// If this is a system resource, don't try to load it from the
// application resources. It is nice to avoid loading application
// resources if we can.
//根据 lp.packageName 获取包名,如果 lp.packageName 为 null,则使用默认包名 DEFAULT_PACKAGE。
String packageName = lp.packageName != null ? lp.packageName : DEFAULT_PACKAGE;
//调用 getAnimationStyleResId(lp) 方法获取动画资源的ID,并将其赋值给变量 resId
int resId = getAnimationStyleResId(lp);
//检查 resId 的高8位是否为0x01。如果是,则将包名重置为默认包名 DEFAULT_PACKAGE。
if ((resId & 0xFF000000) == 0x01000000) {
packageName = DEFAULT_PACKAGE;
}
if (mDebug) {
Slog.v(mTag, "Loading animations: picked package=" + packageName);
}
//调用 AttributeCache.instance().get() 方法获取动画资源,并返回结果。如果获取失败,则返回 null。
return AttributeCache.instance().get(packageName, resId,
com.android.internal.R.styleable.WindowAnimation);
}
-
先来看看获取动画资源ID的方法
getAnimationStyleResId(lp)
/** Returns window animation style ID from {@link LayoutParams} or from system in some cases */ public int getAnimationStyleResId(@NonNull LayoutParams lp) { //把应用侧定义动画赋值给resID int resId = lp.windowAnimations; if (lp.type == LayoutParams.TYPE_APPLICATION_STARTING) { // Note that we don't want application to customize starting window animation. // Since this window is specific for displaying while app starting, // application should not change its animation directly. // In this case, it will use system resource to get default animation. resId = mDefaultWindowAnimationStyleResId; } return resId; }
这个方法实际上就是把应用侧设置的动画给resId。
我们demo中的代码,应用侧赋值动画给LayoutParams的windowAnimationsmLayoutParams.windowAnimations = R.style.MyWindow;
,系统侧赋值给resIdint resId = lp.windowAnimations;
-
再来看看
return AttributeCache.instance().get(packageName, resId, com.android.internal.R.styleable.WindowAnimation);
代码路径:frameworks/base/core/java/com/android/internal/policy/AttributeCache.javapublic Entry get(String packageName, int resId, int[] styleable) { return get(packageName, resId, styleable, UserHandle.USER_CURRENT); } public Entry get(String packageName, int resId, int[] styleable, int userId) { synchronized (this) { Package pkg = mPackages.get(packageName); ArrayMap<int[], Entry> map = null; Entry ent = null; //包名不为空 if (pkg != null) { //从map中获取资源 map = pkg.mMap.get(resId); //map不为空,找到对应的Entry对象,且不为空就返回这个对象 if (map != null) { ent = map.get(styleable); if (ent != null) { return ent; } } //包名为空 } else { Context context; try { //创建一个context context = mContext.createPackageContextAsUser(packageName, 0, new UserHandle(userId)); if (context == null) { return null; } } catch (PackageManager.NameNotFoundException e) { return null; } //创建Package pkg = new Package(context); mPackages.put(packageName, pkg); } //若map为空,创建map if (map == null) { map = new ArrayMap<>(); pkg.mMap.put(resId, map); } //创建Entry对象,并使用obtainStyledAttributes方法从Package的上下文中获取给定资源ID和风格属性数组的属性。 //然后,它将新的Entry对象添加到缓存中。 try { ent = new Entry(pkg.context, pkg.context.obtainStyledAttributes(resId, styleable)); map.put(styleable, ent); } catch (Resources.NotFoundException e) { return null; } return ent; } }
用于获取指定包名、资源ID、风格属性数组和用户ID的Entry对象的函数。如果找不到指定的包,为该包创建一个新的Package对象。如果找不到指定的资源,它将尝试创建一个新的Entry对象并存储在map中。
关键变量解读:
mPackages.put(packageName, pkg);
中的mPackages
指的是private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE);
mPackages
是一个缓存,就是用来存储新的Package对象。pkg.mMap.put(resId, map);
中的mMap
指的是private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>();
mMap
是一个映射,将资源ID映射到另一个映射,这个映射将资源风格属性数组(styleable)映射到Entry对象。
调用启动动画
mWin.startAnimation(a);
用于启动动画,其中参数a
为前面调用loadAnimationAttr方法后的值,即应用侧传递的动画。具体流程看后续的WindowState.startAnimation(),初始化动画参数并通过AnimationAdapter保存动画
检查动画
return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
用户检查指定的窗口容器是否正在进行动画
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
final boolean isAnimating(int flags, int typesToCheck) {
return getAnimatingContainer(flags, typesToCheck) != null;
}
flags
用于确定要检查的动画类型和范围。
typesToCheck
用于确定哪些类型的动画需要检查。
方法内部调用 getAnimatingContainer
方法来获取正在进行动画的窗口容器,并根据返回值判断是否存在符合条件和目标标志的动画。
如果返回值为 true,则说明存在符合条件的动画;如果返回值为 false,则说明不存在符合条件的动画。
初始化动画参数并通过LocalAnimationAdapter保存动画
WindowState.startAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void startAnimation(Animation anim) {
// If we are an inset provider, all our animations are driven by the inset client.
//Insets类型的动画由InsetsSourceProvider相关处理,这个类用于控制窗口动画的边界和剪裁。
//这个接口定义了如何从窗口边界中获取动画的边界信息,以及如何从窗口剪裁中获取动画的剪裁信息。
if (mControllableInsetProvider != null) {
return;
}
//获取显示信息(DisplayInfo),可能包括应用的宽度和高度等信息。
final DisplayInfo displayInfo = getDisplayInfo();
//通过获取到的信息初始化动画
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
//设置动画持续时间,MAX_ANIMATION_DURATION为10s
anim.restrictDuration(MAX_ANIMATION_DURATION);
//按照当前窗口动画的缩放比例来设置缩放动画的持续时间
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
//创建一个新的本地动画适配器(LocalAnimationAdapter),把初始化的anim设置到其中,该类用于管理动画的特定细节。
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
//SurfaceAnimationRunner是本地窗口动画真正的实现类
mWmService.mSurfaceAnimationRunner);
//通过使用前面的adapter启动动画
startAnimation(getPendingTransaction(), adapter);
//最终从WindowContainer.scheduleAnimation()方法
//调用到WindowAnimator.scheduleAnimation()方法安排或触发动画的执行
commitPendingTransaction();
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION);
}
startAnimation(Animation anim)
,前面把应用定义的动画传递到了anim
,然后对其进行了参数设置,创建了一个AnimationAdapter对象,把动画包装到了WindowAnimationSpec中,最后通过startAnimation传递这个AnimationAdapter的对象。
WindowAnimationSpec的作用主要是定义窗口动画的各种属性和行为。这个类包含了一些字段,这些字段定义了动画的类型、持续时间、插值器等。
当WMS需要为一个窗口启动动画时,它会创建一个WindowAnimationSpec对象,并将这个对象传递给SurfaceAnimationRunner。然后,SurfaceAnimationRunner会根据这个WindowAnimationSpec对象的属性来创建和执行动画。
commitPendingTransaction();
方法会最终调用到WindowAnimator.scheduleAnimation()方法,协调动画与窗口的显示。
传递启动动画需要的参数
WindowContainer.startAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
startAnimation(t, anim, hidden, type, animationFinishedCallback,
null /* adapterAnimationCancelledCallback */, null /* snapshotAnim */);
}
/**
* 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.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
* @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
* snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
this, type, 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,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
从WindowState的startAnimation方法逐步调用到WindowContainer的startAnimation方法
-
入参含义
t
:这是一个对象,用于描述一系列的窗口操作,例如移动、调整大小、绘制等。这些操作在WMS中排队,并在适当的时机应用到窗口上。
anim
:这是对动画进行封装的类。它包含了一些关于如何开始、更新和结束动画的信息。传递的就是前面WindowState.startAnimation()中创建的LocalAnimationAdapter对象。
hidden
:这个布尔值表示窗口是否隐藏。如果窗口是隐藏的,那么就不会显示动画。
type
:这个整数代表了动画的类型。这里我们传递的是ANIMATION_TYPE_WINDOW_ANIMATION
,即图层上显示的动画类型window_animation。
animationFinishedCallback
和animationCancelledCallback
:这两个是回调函数,分别在动画完成和动画取消时被调用。
snapshotAnim
:这个参数是给定动画的快照。如果参数为null,那么就表示没有快照。
从WindowContainer的startAnimation方法调用中,我们可以看出这里animationFinishedCallback
、animationCancelledCallback
和snapshotAnim
传递过来的均为null
-
代码含义
关键的代码只有这一句mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback, animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
这行代码调用了SurfaceAnimator的startAnimation方法来启动动画。SurfaceAnimator的作用主要是控制窗口动画,它是窗口动画的中控,通过操控mLeash对象来实现窗口的大小、位置、透明度等动画属性的改变。这个方法需要一系列参数,包括上面解释的所有参数,还有一个SurfaceFreezer对象mSurfaceFreezer
,它可以在动画开始时冻结窗口的更新,以防止在动画过程中窗口的内容闪烁。
mSurfaceAnimator和mSurfaceFreezer是在WindowContainer的构造方法中初始化的class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable, InsetsControlTarget { ...... WindowContainer(WindowManagerService wms) { mWmService = wms; mTransitionController = mWmService.mAtmService.getTransitionController(); mPendingTransaction = wms.mTransactionFactory.get(); mSyncTransaction = wms.mTransactionFactory.get(); mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); mSurfaceFreezer = new SurfaceFreezer(this, wms); } ...... }
创建leash并通过LocalAnimationAdapter启动动画
SurfaceAnimator.startAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
/**
* Starts an animation.
*
* @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
* component responsible for running the animation. It runs the animation with
* {@link AnimationAdapter#startAnimation} once the hierarchy with
* the Leash has been set up.
* @param hidden Whether the container holding the child surfaces is currently visible or not.
* This is important as it will start with the leash hidden or visible before
* handing it to the component that is responsible to run the animation.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
* @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
* snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
//开始新的动画之前,取消之前的动画
//参数含义:t(是一个事务对象),true(表示动画正在重新启动),和true(表示向前取消)
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
//初始化参数,把WindowContainer.startAnimation中传递的参数赋值给对应变量
mAnimation = anim;
mAnimationType = type;
mSurfaceAnimationFinishedCallback = animationFinishedCallback;
mAnimationCancelledCallback = animationCancelledCallback;
//获取当前窗口的SurfaceControl
final SurfaceControl surface = mAnimatable.getSurfaceControl();
//没有surface,则取消当前的动画
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
cancelAnimation();
return;
}
//调用SurfaceFreezer中takeLeashForAnimation()获取mLeash,但是SurfaceFreezer中没有被初始化,所以这里的mLeash还是为null
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
if (mLeash == null) {
//创建mLeash
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden, mService.mTransactionFactory);
//创建动画“leash”后执行的一些操作,包括重置图层、重新分配图层以及重置Surface的位置
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
//处理动画开始时进行一些设置和准备工作
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
return;
}
//将leash传给AnimationAdapter,执行动画
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
mAnimation.dump(pw, "");
ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);
}
//获取一个快照,并使用该快照来执行动画,我们这里snapshotAnim为null,因此不涉及
if (snapshotAnim != null) {
mSnapshot = freezer.takeSnapshotForAnimation();
if (mSnapshot == null) {
Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);
return;
}
mSnapshot.startAnimation(t, snapshotAnim, type);
}
}
入参就是前面WindowContainer.startAnimation中传递的参数。
获取当前窗口的surface
final SurfaceControl surface = mAnimatable.getSurfaceControl();
mAnimatable是Animatable接口的对象,WindowContainer实现了Animatable接口。
在WindowContainer构造方法中初始化mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
而SurfaceAnimator的构造方法是
SurfaceAnimator(Animatable animatable,
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
也就是说实际上是把this
赋值给了mAnimatable
,因此mAnimatable
就代表了当前的窗口。
获取Leash
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
根据freezer是否为null来确定mLeash的值,我们这里freezer
是从WindowContainer.startAnimation方法中传递过来的mSurfaceFreezer
,这个变量在WindowContainer的构造方法中初始化mSurfaceFreezer = new SurfaceFreezer(this, wms);
,因此mSurfaceFreezer
不为null
,即freezer
不为null
,freezer != null
为true
,所以走freezer.takeLeashForAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceFreezer.java
SurfaceControl mLeash;
/**
* Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
* By transferring the leash, this will no longer try to clean-up the leash when finished.
*/
SurfaceControl takeLeashForAnimation() {
SurfaceControl out = mLeash;
mLeash = null;
return out;
}
mLeash在SurfaceFreezer类中并没有初始化,因此我们的mLeash值为null,所以out的值同样为null,最终SurfaceAnimator类中的mLeash获取到的值为null
创建Leash
mLeash为null,使用createAnimationLeash方法创建Leash
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden, mService.mTransactionFactory);
入参含义
mAnimatable
:当前窗口。
surface
:当前窗口的surface。
t
:一个事务对象,用于执行一系列操作。
type
:动画类型。
mAnimatable.getSurfaceWidth()
、mAnimatable.getSurfaceHeight()
:窗口surface尺寸的参数。
0 /* x */
、 0 /* y */
:坐标位置
hidden
:一个布尔值,表示是否隐藏。
mService.mTransactionFactory
:一个事务工厂对象,用于创建新的事务。
SurfaceAnimator.createAnimationLeash()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
boolean hidden, Supplier<Transaction> transactionFactory) {
/* log add start*/
Slog.i("WindowManager:","createAnimationLeash type = " + animationTypeToString(type) , new Exception());
/* log add end*/
ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);
//通过SurfaceControl.Builder创建leash
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
.setName(surface + " - animation-leash of " + animationTypeToString(type))
// TODO(b/151665759) Defer reparent calls
// We want the leash to be visible immediately because the transaction which shows
// the leash may be deferred but the reparent will not. This will cause the leashed
// surface to be invisible until the deferred transaction is applied. If this
// doesn't work, you will can see the 2/3 button nav bar flicker during seamless
// rotation.
.setHidden(hidden)
.setEffectLayer()
.setCallsite("SurfaceAnimator.createAnimationLeash");
//通过前面的SurfaceControl.Builder创建leash
final SurfaceControl leash = builder.build();
//其他属性设置
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
//当前窗口的surface重新绑定到新创建的leash上
t.reparent(surface, leash);
return leash;
}
前面说过animatable为Animatable接口对象,WindowContainer为该接口的实现类,即animatable表示当前窗口,就是我们图层里面的test-window,这个方法主要就是把创建好的leash图层加入到test-window和它父亲节点WindowToken之间。
下面我们解读一下,leash是如何创建并加入其中的
通过SurfaceControl.Builder创建leash
-
animatable.makeAnimationLeash()
代码路径: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.javapublic Builder makeAnimationLeash() { return makeSurface().setContainerLayer(); }
创建一个图层作为容器layer
-
setParent(animatable.getAnimationLeashParent())
这段代码我们分成两个部分来看,即setParent()
和getAnimationLeashParent()
1.setParent()
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java/** * Set a parent surface for our new SurfaceControl. * * Child surfaces are constrained to the onscreen region of their parent. * Furthermore they stack relatively in Z order, and inherit the transformation * of the parent. * * @param parent The parent control. */ @NonNull public Builder setParent(@Nullable SurfaceControl parent) { mParent = parent; return this; }
这个段代码很简单,就是给当前SurfaceControl设置一个父SurfaceControl。
2.getAnimationLeashParent()
代码路径: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java@Override public SurfaceControl getAnimationLeashParent() { return getParentSurfaceControl(); } /* * @return The SurfaceControl parent for this containers SurfaceControl. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getParentSurfaceControl() { final WindowContainer parent = getParent(); if (parent == null) { return null; } return parent.getSurfaceControl(); } /** * @return The SurfaceControl for this container. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getSurfaceControl() { return mSurfaceControl; }
简单来说,就是获取当前窗口父SurfaceControl。
那么合起来
setParent(animatable.getAnimationLeashParent())
的意思就是,把当前新创建的SurfaceControl(leash)的父亲设置为当前窗口父亲的SurfaceControl。
即此时leash图层和当前窗口test-window的父亲均是WindowToken,两人还在当兄弟。
简图如下:
-
setEffectLayer()
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java/** * Indicate whether an 'EffectLayer' is to be constructed. * * An effect layer behaves like a container layer by default but it can support * color fill, shadows and/or blur. These layers will not have an associated buffer. * When created, this layer has no effects set and will be transparent but the caller * can render an effect by calling: * - {@link Transaction#setColor(SurfaceControl, float[])} * - {@link Transaction#setBackgroundBlurRadius(SurfaceControl, int)} * - {@link Transaction#setShadowRadius(SurfaceControl, float)} * * @hide */ public Builder setEffectLayer() { mFlags |= NO_COLOR_FILL; //清空缓冲区设置 unsetBufferSize(); return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK); }
设置为EffectLayer。它是一种特殊类型的SurfaceControl层,它默认表现得像一个容器层,但可以支持颜色填充、阴影和/或模糊效果。
这个EffectLayer主要就是用于实现一些视觉效果。
默认的注释里面也说明可以使用这些方法来渲染一个效果:
Transaction#setColor(SurfaceControl, float[]):使用给定的颜色数组设置该层的颜色。
Transaction#setBackgroundBlurRadius(SurfaceControl, int):设置背景模糊的半径。
Transaction#setShadowRadius(SurfaceControl, float):设置阴影的半径。
final SurfaceControl leash = builder.build();
最后通过build()
方法创建leash
当前窗口的surface重新绑定到新创建的leash上
t.reparent(surface, leash);
这里的surface
指的就是从前面传递的当前窗口的SurfaceControl。
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
* parent Surface.
*
* @param sc The SurfaceControl to reparent
* @param newParent The new parent for the given control.
* @return This Transaction
*/
@NonNull
public Transaction reparent(@NonNull SurfaceControl sc,
@Nullable SurfaceControl newParent) {
//检查传入的SurfaceControl对象是否满足某些预设条件
checkPreconditions(sc);
long otherObject = 0;
if (newParent != null) {
//检查新父对象是否被释放。如果已经被释放,那么它会抛出异常。
newParent.checkNotReleased();
//新父对象不为null且未被释放,那么将新父对象的Native对象赋值给otherObject。
otherObject = newParent.mNativeObject;
}
//传入了三个参数:1.当前对象的Native对象 2.被重新设置父对象的SurfaceControl的Native对象 3.新父对象的Native对象。
//用于实现重新设置父对象的具体操作。
nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
//把被重新设置父对象的SurfaceControl和新父对象存储到mReparentedSurfaces这个map中。
mReparentedSurfaces.put(sc, newParent);
return this;
}
@NonNull SurfaceControl sc
: 表示要被重新设置父对象的SurfaceControl对象。这个参数不能为null。
@Nullable SurfaceControl newParent
: 表示新的父SurfaceControl对象。可以为null,表示没有新的父对象。
这个方法主要就是把当前窗口的SurfaceControl的父亲,修改为leash。
mReparentedSurfaces
是ArrayMap对象,以键值对的形式临时存储父子关系,key值存储SurfaceControl对象,value为其父SurfaceControl对象。
此时mReparentedSurfaces
的键值对为:
key:Surface(name=8aabddb test-window)/@0x1038e9a
(当前窗口的SurfaceControl)
value:Surface(name=Surface(name=8aabddb test-window)/@0x1038e9a - animation-leash of window_animation)/@0x38e37a8
(动画图层leash)
即此时leash图层变成了当前窗口test-window图层的父亲,如简易图所示:
曾经我们是兄弟,如今我是你爸爸~
需要注意的是,test-window和WindowToken容器之前的关系并未改变,创建leash图层的过程只是改变的是surface之间的关系。
即leash图层是test-window图层的父亲,WindowToken的图层是leash图层的父亲,但是test-window的父亲仍然是WindowToken。
实际关系情况如下图所示:
所以WindowToken和leash实际上是表面父子,leash和test-window也是表面父子
leash的surface调整
mAnimatable.onAnimationLeashCreated(t, mLeash);
入参是一个事务(用于操作窗口系统的底层API)和一个SurfaceControl对象(表示一个可以控制和操作Surface的接口)
把创建好的mLeash传递到onAnimationLeashCreated方法中,做一些Surface调整操作。
该方法实现在WindowContainer中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void reassignLayer(Transaction t) {
final WindowContainer parent = getParent();
if (parent != null) {
parent.assignChildLayers(t);
}
}
void resetSurfacePositionForAnimationLeash(Transaction t) {
t.setPosition(mSurfaceControl, 0, 0);
final SurfaceControl.Transaction syncTransaction = getSyncTransaction();
if (t != syncTransaction) {
// Avoid restoring to old position if the sync transaction is applied later.
syncTransaction.setPosition(mSurfaceControl, 0, 0);
}
mLastSurfacePosition.set(0, 0);
}
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
mAnimationLeash = leash;
reassignLayer(t);
// Leash is now responsible for position, so set our position to 0.
resetSurfacePositionForAnimationLeash(t);
}
这段代码主要用于在创建新的leash时,重置动画目标的位置,并初始化一些动画相关的状态。同时,可能还用于重新分配或者设置子容器的图层。
首先,在新的动画(leash)被创建时被调用。在这个方法中,首先将mLastLayer
(可能表示上一个图层或者上一个动画目标)设置为-1,然后保存传入的leash
到mAnimationLeash
(后面removeLeash流程中会用到mAnimationLeash
)。
之后,调用reassignLayer(t)
方法,这个方法获取这个视图的父容器,如果父容器存在,那么就调用父容器的assignChildLayers(t)
方法(用于调整其所有child的z-order)。
最后,为了确保leash现在位置的控制,调用resetSurfacePositionForAnimationLeash(t)
方法将Surface的位置重置为(0,0),重置界面元素的位置以便进行动画。
注:Z-order也被称为深度顺序(depth order)或Z轴顺序,它用于确定图层(Layers)在屏幕上的堆叠顺序。简单来说,Z-order就是图层在Z轴上的位置,Z轴位置越低,图层越在底层,Z轴位置越高,图层越在顶层。
处理动画开始时进行一些设置和准备工作
mAnimatable.onLeashAnimationStarting(t, mLeash);
入参同样是一个事务(用于操作窗口系统的底层API)和一个SurfaceControl对象(表示一个可以控制和操作Surface的接口)
onLeashAnimationStarting方法是在ActivityRecord中实现的。
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@Override
public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
//1.将正在启动或者有动画效果的Activity添加到列表中,以便于管理和控制这些Activity的动画效果。
mAnimatingActivityRegistry.notifyStarting(this);
}
// If the animation needs to be cropped then an animation bounds layer is created as a
// child of the root pinned task or animation layer. The leash is then reparented to this
// new layer.
//2.否需要创建一个动画边界层
if (mNeedsAnimationBoundsLayer) {
//设置临时矩形为空
mTmpRect.setEmpty();
//调用方法检查当前的活动转移是否在任务内部。
//如果是,则获取任务的边界到临时矩形mTmpRect。如果不是,则获取RootTask的边界。
if (getDisplayContent().mAppTransitionController.isTransitWithinTask(
getTransit(), task)) {
task.getBounds(mTmpRect);
} else {
final Task rootTask = getRootTask();
if (rootTask == null) {
return;
}
// Set clip rect to root task bounds.
rootTask.getBounds(mTmpRect);
}
//创建动画边界层
mAnimationBoundsLayer = createAnimationBoundsLayer(t);
// Crop to root task bounds.
//设置leash的层为0
//leash将被放置在Z轴的最底层,如果有其他层级的SurfaceControl对象,它们将会覆盖在leash之上。
t.setLayer(leash, 0);
//并设置AnimationBoundsLayer的层为上一个层的值,保证leash在AnimationBoundsLayer下面
t.setLayer(mAnimationBoundsLayer, getLastLayer());
// Reparent leash to animation bounds layer.
//重新将leash的父节点设置为动画边界层。
t.reparent(leash, mAnimationBoundsLayer);
}
}
private SurfaceControl createAnimationBoundsLayer(Transaction t) {
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer");
final SurfaceControl.Builder builder = makeAnimationLeash()
//给AnimationBoundsLayer设置父节点为Leash的父节点
//即把动画边界层的父节点设置为windowToken
.setParent(getAnimationLeashParent())
.setName(getSurfaceControl() + " - animation-bounds")
.setCallsite("ActivityRecord.createAnimationBoundsLayer");
final SurfaceControl boundsLayer = builder.build();
t.show(boundsLayer);
return boundsLayer;
}
这个方法其实主要就是做了两件事:
1.根据mAnimatingActivityRegistry的值判断,是否需要把有动画效果的Activity添加到列表中
2.根据mNeedsAnimationBoundsLayer的值判断,否需要创建一个动画边界层
createAnimationBoundsLayer
就是创建了一个SurfaceControl。
getLastLayer()
用于返回当前窗口的最高(或最后)层级。假设我们有一个窗口管理系统中,窗口的层级从0开始编号。当一个新窗口创建时,它可能被赋予层级0。然后,如果这个新窗口被另一个窗口覆盖,那么新窗口的层级可能会更新为1,依此类推。
通过使用AnimationBoundsLayer,可以定义一个矩形区域,该区域可以作为动画的边界。当动画开始时,它只在该定义的区域内显示,不会超出这个边界。AnimationBoundsLayer的主要作用是限制动画的显示区域,以确保动画不会影响到应用程序的其他部分。
将leash传给LocalAnimationAdapter,执行动画
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
mAnimation
是AnimationAdapter接口的对象,调用其startAnimation
方法,传递mLeash(动画)、t(事务)、type(动画类型)和mInnerAnimationFinishedCallback(回调函数)。
mAnimation
的值是前面WindowContainer.startAnimation传递的anim
,这个anim
实际上就是WindowState.startAnimation中创建的LocalAnimationAdapter对象,因此接口AnimationAdapter的方法startAnimation在LocalAnimationAdapter中实现。
代码路径:frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
-
mAnimator
是SurfaceAnimationRunner对象,会根据这个WindowAnimationSpec对象的属性来创建和执行动画。 -
mSpec
是WindowAnimationSpec对象,也就是窗口动画的各种属性和行为。
这两个参数都是在LocalAnimationAdapter构造方法中初始化的LocalAnimationAdapter(AnimationSpec spec, SurfaceAnimationRunner animator) { mSpec = spec; mAnimator = animator; }
在前面说过的WindowState.startAnimation()流程中调用的
final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, 0 /* windowCornerRadius */), mWmService.mSurfaceAnimationRunner);
WindowAnimationSpec构造方法对一下参数进行了初始化
public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame, float windowCornerRadius) { this(animation, position, null /* rootTaskBounds */, canSkipFirstFrame, ROOT_TASK_CLIP_NONE, false /* isAppAnimation */, windowCornerRadius); } public WindowAnimationSpec(Animation animation, Point position, Rect rootTaskBounds, boolean canSkipFirstFrame, int rootTaskClipMode, boolean isAppAnimation, float windowCornerRadius) { mAnimation = animation; if (position != null) { mPosition.set(position.x, position.y); } mWindowCornerRadius = windowCornerRadius; mCanSkipFirstFrame = canSkipFirstFrame; mIsAppAnimation = isAppAnimation; mRootTaskClipMode = rootTaskClipMode; if (rootTaskBounds != null) { mRootTaskBounds.set(rootTaskBounds); } }
这里从代码中我们可以看到
mWindowCornerRadius
的值是0
,mRootTaskBounds
的值是null
,mRootTaskClipMode
的值是ROOT_TASK_CLIP_NONE
。 -
() -> finishCallback.onAnimationFinished(type, this))
当动画完成时,这个函数会被调用,并执行finishCallback.onAnimationFinished(type, this)
这里LocalAnimationAdapter.startAnimation方法的作用实际上就是播放完动画后,通过回调函数移除动画,这里我们关注播放流程。
通过SurfaceAnimationRunner启动动画
SurfaceAnimationRunner.startAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
//创建RunningAnimation对象,把传递的参数赋值给RunningAnimation
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
//判断一个窗口动画是否应用了扩展动画,通过查代码发现requiresEdgeExtension(a)的默认值为false
//requiresEdgeExtension(a)为true的情况涉及ExtendAnimation,这里我们不讨论
boolean requiresEdgeExtension = requiresEdgeExtension(a);
//requiresEdgeExtension为true
if (requiresEdgeExtension) {
......
}
//requiresEdgeExtension为false
if (!requiresEdgeExtension) {
//mPendingAnimations是一个ArrayMay
//将创建的runningAnim动画对象添加到mPreProcessingAnimations映射中,使用animationLeash作为键
mPendingAnimations.put(animationLeash, runningAnim);
//mAnimationStartDeferred判断动画是否延迟启动
//mPreProcessingAnimations.isEmpty()判断动画是否为空
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
//通过Choreographer请求Vsync信号,接收到Vsync信号回调this::startAnimations
//即使用postFrameCallback在下一帧绘制完成后回调this::startAnimations
mChoreographer.postFrameCallback(this::startAnimations);
}
// Some animations (e.g. move animations) require the initial transform to be
// applied immediately.
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
}
requiresEdgeExtension
为true
的情况涉及ExtendAnimation
用于扩展动画效果。
这里我们只关注requiresEdgeExtension
为false
的情况,下面我们来说下这个方法中所涉及的关键代码
创建RunningAnimation对象
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, finishCallback);
private static final class RunningAnimation {
final AnimationSpec mAnimSpec;
final SurfaceControl mLeash;
final Runnable mFinishCallback;
ValueAnimator mAnim;
@GuardedBy("mCancelLock")
private boolean mCancelled;
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
mAnimSpec = animSpec;
mLeash = leash;
mFinishCallback = finishCallback;
}
}
从代码中可以看出RunningAnimation只是存储前面传递的a
(窗口动画的各种属性和行为)、animationLeash
(动画)和finishCallback
(回调函数)
动画的播放条件
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty())
我们主要关注这个条件中的mAnimationStartDeferred
变量,这个变量主要是用来控制动画的播放时机的。mPreProcessingAnimations
是个ArrayMay,这个只在前面requiresEdgeExtension
为true
时逻辑中有涉及,这里不讨论。
在SurfaceAnimationRunner类中设置mAnimationStartDeferred
的值的方法有两个,deferStartingAnimations()
和continueStartingAnimations()
/**
* Defers starting of animations until {@link #continueStartingAnimations} is called. This
* method is NOT nestable.
*
* @see #continueStartingAnimations
*/
void deferStartingAnimations() {
synchronized (mLock) {
mAnimationStartDeferred = true;
}
}
/**
* Continues starting of animations.
*
* @see #deferStartingAnimations
*/
void continueStartingAnimations() {
synchronized (mLock) {
mAnimationStartDeferred = false;
if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
}
这两个方法,是在前面窗口添加流程中handleAppTransitionReady()方法里调用
void handleAppTransitionReady() {
......
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
......
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
......
}
从代码中可以看出这个方法,最后通过调用continueStartingAnimations()
,把mAnimationStartDeferred
设置为了false
我们在结合前面startAnimation中的这段代码来看
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
可以推测出,deferStartingAnimations()
方法用来将动画的启动时间推迟到 continueStartingAnimations()
方法被调用。
这意味着,当你调用deferStartingAnimations()
后,所有新的动画不会立即开始,它们会被放入待处理列表(在 mPendingAnimations
中)。
continueStartingAnimations()
方法则用于恢复动画的启动。当这个方法被调用时,动画的启动不再被推迟,所有待处理的动画会开始播放。
这两个方法通常用于动画的复杂控制,例如,在数据加载或其他后台任务完成之前,你可能希望延迟动画的播放,直到这些任务完成。
简单来说,这两个方法的主要作用是控制何时开始播放动画。
启动动画并监听动画的播放和结束
mChoreographer.postFrameCallback(this::startAnimations);
通过Choreographer的postFrameCallback
方法在下一帧绘制完成后回调this::startAnimations
,其实就是通过请求和接收 Vsync 信号播放动画。
实际调用的就是SurfaceAnimationRunner中的startAnimations方法
private void startAnimations(long frameTimeNanos) {
synchronized (mLock) {
if (!mPreProcessingAnimations.isEmpty()) {
// We only want to start running animations once all mPreProcessingAnimations have
// been processed to ensure preprocessed animations start in sync.
// NOTE: This means we might delay running animations that require preprocessing if
// new animations that also require preprocessing are requested before the previous
// ones have finished (see b/227449117).
return;
}
//启动所有待处理的动画
startPendingAnimationsLocked();
}
//用于设置电源管理器的功率增强
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
}
mPreProcessingAnimations
是个ArrayMay,这个只在前面requiresEdgeExtension
为true
时逻辑中有涉及,这里不讨论。
这个方法的作用是:当所有预处理的动画都处理完毕后,它会启动所有待处理的动画。同时,它还设置了一个电源管理器的功率增强。
这里我们主要关注startPendingAnimationsLocked();
@GuardedBy("mLock")
private void startPendingAnimationsLocked() {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
//开启动画列表中的每个动画
startAnimationLocked(mPendingAnimations.valueAt(i));
}
//清理动画列表
mPendingAnimations.clear();
}
@GuardedBy("mLock")
private void startAnimationLocked(RunningAnimation a) {
//创建了一个ValueAnimator(属性动画)
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
anim.overrideDurationScale(1.0f);//设置动画的持续时间比例为1.0,就是把动画将按照其原始速度运行。
anim.setDuration(a.mAnimSpec.getDuration());//设置动画的持续时间为a.mAnimSpec的持续时间。
// 为动画(anim)添加一个更新监听器。当动画的属性值发生变化时,会触发这个监听器的回调函数。
anim.addUpdateListener(animation -> {
//使用mCancelLock对象作为锁,确保同一时间只有一个线程能够执行以下代码块,避免并发访问和修改数据造成的问题。
synchronized (mCancelLock) {
//检查a对象的mCancelled属性,如果mCancelled为false,表示动画没有被取消,则继续执行下面的代码。
if (!a.mCancelled) {
final long duration = anim.getDuration();//获取动画的持续时间。
long currentPlayTime = anim.getCurrentPlayTime();//获取动画当前的播放时间。
//如果当前播放时间超过了持续时间,将当前播放时间设置为持续时间。
if (currentPlayTime > duration) {
currentPlayTime = duration;
}
//根据动画的当前时间点获取相应的变换效果,并将这些效果应用到指定的Surface对象(leash图层)上,包括位置、透明度、裁剪和圆角等。
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// Transaction will be applied in the commit phase.
//执行下一帧动画
scheduleApplyTransaction();
});
//为anim添加一个监听器。监听器是AnimatorListenerAdapter的一个实例,需要重写onAnimationStart和onAnimationEnd两个方法作为动画开始和结束的回调。
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.
//设置a.mLeash的透明度为1(完全不透明)。
mFrameTransaction.setAlpha(a.mLeash, 1);
}
}
}
//当动画结束时,这个方法会被调用。
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
//从mRunningAnimations这个映射(或字典)中移除与a.mLeash相关的动画。
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);
}
}
}
}
});
//将当前创建的ValueAnimator赋值给RunningAnimation中的ValueAnimator
a.mAnim = anim;
//将动画与相关的对象(如动画的状态、回调等)添加到mRunningAnimations这个映射中
//键为a.mLeash(leash图层),值为RunningAnimation对象
mRunningAnimations.put(a.mLeash, a);
//开始动画
anim.start();
//判断是否可以跳过第一帧
if (a.mAnimSpec.canSkipFirstFrame()) {
// If we can skip the first frame, we start one frame later.
//如果我们可以跳过第一帧,我们将在一帧之后开始
//当前播放时间设置为Choreographer的帧间隔纳秒值除以毫秒值
anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
}
// Immediately start the animation by manually applying an animation frame. Otherwise, the
// start time would only be set in the next frame, leading to a delay.
//执行一帧动画
anim.doAnimationFrame(mChoreographer.getFrameTime());
}
这个方法的整体流程就是
- 创建
ValueAnimator
对象anim
,设置基本属性和监听器 - 将当前动画添加到
mRunningAnimations
列表中(后续移除动画逻辑中,取消动画流程用到) anim.start()
播放动画,先执行anim.addListener
中的onAnimationStart
。- 通过
anim.doAnimationFrame(mChoreographer.getFrameTime())
播放一帧动画。 - 之后
anim.addUpdateListener
监听到动画属性发生变化,调用applyTransformation
根据动画的当前时间点获取相应的变换效果,再使用scheduleApplyTransaction
调用下一帧动画。 - 最后动画播放结束时,执行
anim.addListener
中的onAnimationEnd
,通过mAnimationThreadHandler.post(a.mFinishCallback);
执行回调函数收尾。
我们来看看这个代码中的一些关键方法。
-
applyTransformation(a, mFrameTransaction, currentPlayTime);
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); }
a.mLeash
就是leash图层,这个mLeash
和mAnimSpec
是前面SurfaceAnimationRunner.startAnimation方法中创建RunningAnimation时保存的。
mAnimSpec
是AnimationSpec接口对象,apply()
方法是在WindowAnimationSpec实现的,把leash图层传递了过去。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowAnimationSpec.java@Override public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) { //从线程局部变量mThreadLocalTmps中获取一个TmpValues对象,并将其存储在局部变量tmp中 final TmpValues tmp = mThreadLocalTmps.get(); //清除tmp.transformation中的所有变换 tmp.transformation.clear(); //根据当前时间戳获取一个动画变换,并将其存储在tmp.transformation中 mAnimation.getTransformation(currentPlayTime, tmp.transformation); //对动画变换的矩阵进行平移 tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y); //使用获取的矩阵设置界面元素的变换 t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats); //设置界面元素的透明度 t.setAlpha(leash, tmp.transformation.getAlpha()); boolean cropSet = false; //ROOT_TASK_CLIP_NONE用来表示不进行任何剪裁的 if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) { if (tmp.transformation.hasClipRect()) {//检查tmp.transformation是否有剪裁矩形(ClipRect) //设置窗口的剪裁区域并标记cropSet为true final Rect clipRect = tmp.transformation.getClipRect(); accountForExtension(tmp.transformation, clipRect); t.setWindowCrop(leash, clipRect); cropSet = true; } } else { //设置一个临时矩形,mRootTaskBounds在WindowAnimationSpec构造方法中初始化 mTmpRect.set(mRootTaskBounds); if (tmp.transformation.hasClipRect()) {//检查tmp.transformation是否有剪裁矩形 //取临时矩形和剪裁矩形的交集,形成新矩阵存储至mTmpRect mTmpRect.intersect(tmp.transformation.getClipRect()); } //设置窗口的剪裁区域并标记cropSet为true accountForExtension(tmp.transformation, mTmpRect); t.setWindowCrop(leash, mTmpRect); cropSet = true; } // We can only apply rounded corner if a crop is set, as otherwise the value is meaningless, // since it doesn't have anything it's relative to. //如果设置了剪裁(由之前的cropSet标记),并且动画有圆角设置,且窗口的圆角半径大于0 if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) { //则设置界面元素的圆角 t.setCornerRadius(leash, mWindowCornerRadius); }
这个方法的作用是根据给定的动画(leash)和变换信息更新界面元素的属性,包括其位置、透明度、剪裁和圆角。
-
scheduleApplyTransaction()
private void scheduleApplyTransaction() { if (!mApplyScheduled) { mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, null /* token */); mApplyScheduled = true; } }
通过标志位
mApplyScheduled
判断是否调用mChoreographer对象的postCallback方法,这个方法用于在UI线程中安排一个回调。mApplyTransactionRunnable
这是一个Runnable对象,它定义了当回调被触发时要执行的任务。之后把标志位mApplyScheduled
置为true,表示事务已经被调度。
这个mApplyTransactionRunnable
其实就是SurfaceAnimationRunner中定义的private final Runnable mApplyTransactionRunnable = this::applyTransaction; private void applyTransaction() { //将当前的事务设置为动画事务 mFrameTransaction.setAnimationTransaction(); //调用了mChoreographer对象的getVsyncId方法来获取vsync的ID, //使用这个ID来设置mFrameTransaction的帧时间线vsync。 //这是为了确保事务在正确的vsync时间点被应用。 mFrameTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId()); //提交之前设置的事务。 mFrameTransaction.apply(); //将mApplyScheduled标志位设置为false。 mApplyScheduled = false; }
-
mAnimationThreadHandler.post(a.mFinishCallback);
这个a.mFinishCallback
,实际上就是前面LocalAnimationAdapter.startAnimation方法中传递的() -> finishCallback.onAnimationFinished(type, this)
,调用这个函数开始动画的结束流程,具体看后续的移除流程。
立即应用一次动画变换
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
同样是调用到了WindowAnimationSpec.apply方法
对当前动画(runningAnim)应用一个变换(可能是缩放、旋转或平移等,0
是当前的播放时间。这个方法是立即应用初始变换,为了让动画可以立即看到效果。
通过WindowAnimator协调动画和窗口的显示
我们回到WindowState.startAnimation()方法中的最后调用了commitPendingTransaction();
调用到了WindowContainer的commitPendingTransaction()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
public void commitPendingTransaction() {
scheduleAnimation();
}
/**
* Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
* will be applied.
*/
void scheduleAnimation() {
mWmService.scheduleAnimationLocked();
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void scheduleAnimationLocked() {
mAnimator.scheduleAnimation();
}
最终调用到了WindowAnimator.scheduleAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
//mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排
mAnimationFrameCallbackScheduled = true;
//每一帧被绘制时,回调mAnimationFrameCallback
mChoreographer.postFrameCallback(mAnimationFrameCallback);
}
}
这个方法的主要作用是确保动画帧回调被正确地安排,以便在每一帧绘制时执行,可以确保动画在每一帧都被调用,从而平滑地更新和显示动画。
WindowAnimator构造方法
mAnimationFrameCallback
是在WindowAnimator构造方法中初始化的,而WindowAnimator构造方法是在WindowManagerService构造方法中初始化的
WindowAnimator(final WindowManagerService service) {
mService = service;
mContext = service.mContext;
mPolicy = service.mPolicy;
mTransaction = service.mTransactionFactory.get();
//使用runWithScissors方法,确保Runnable对象在正确的线程上执行,并提供了超时机制来处理长时间运行的操作
//创建一个Choreographer实例,并赋值给mChoreographer,处理动画和渲染更新
service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
//实现Choreographer.FrameCallback接口的doFrame方法
//入参为frameTimeNs 表示当前帧的时间戳,以纳秒为单位
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
//mAnimationFrameCallbackScheduled设置为false,表示该回调不再被安排
mAnimationFrameCallbackScheduled = false;
//从Choreographer对象中获取当前垂直同步(VSync)的ID
final long vsyncId = mChoreographer.getVsyncId();
//执行实际的动画逻辑
animate(frameTimeNs, vsyncId);
//mNotifyWhenNoAnimation:表示在没有动画时,是否通知某些东西
//mLastRootAnimating:表示当前是否有正在进行的顶层(root层)动画
//即!mLastRootAnimating表示没有正在进行的顶层动画,这个在animate方法中会赋值
if (mNotifyWhenNoAnimation && !mLastRootAnimating) {
//唤醒所有等待在mService.mGlobalLock上的线程
mService.mGlobalLock.notifyAll();
}
}
};
}
执行实际的动画逻辑
animate(frameTimeNs, vsyncId);
private void animate(long frameTimeNs, long vsyncId) {
if (!mInitialized) {
return;
}
// Schedule next frame already such that back-pressure happens continuously.
//调用下一帧动画
scheduleAnimation();
//获取RootWindowContainer根容器
final RootWindowContainer root = mService.mRoot;
//更新当前时间为给定的frameTimeNs除以毫秒TimeUtils.NANOS_PER_MS
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
//运行窗口更新一个标志位
mBulkUpdateParams = 0;
//表示显示方向的更新是否完成
root.mOrientationChangeComplete = true;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
//开启surface事务
mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
//处理所有延迟移除的一些容器
root.handleCompleteDeferredRemoval();
//创建accessibilityController
final AccessibilityController accessibilityController =
mService.mAccessibilityController;
//更新每个显示内容的窗口
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = root.getDisplayContent(displayId);
// Update animations of all applications, including those associated with
// exiting/removed apps.
//实际就是调用了WindowContainer中的forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);
//该方法根据traverseTopToBottom决定遍历的顺序,来对所有的容器执行callback回调函数。
dc.updateWindowsForAnimator();
//显示Surface
dc.prepareSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = root.getDisplayContent(displayId);
//检查App中的所有窗口是否都已绘制,并在需要时显示它们。
dc.checkAppWindowsReadyToShow();
//如果accessibilityController有回调,则绘制放大的区域边界。
if (accessibilityController.hasCallbacks()) {
accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
mTransaction);
}
}
//取消当前的动画
//就是通过mChoreographer移除mAnimationFrameCallback
cancelAnimation();
//绘制水印
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
}
//检查根窗口容器是否有待处理的布局更改,特别是与壁纸相关的更改
final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
//方法的开头已经将mBulkUpdateParams置为0,root.mOrientationChangeComplete设置为true
//root.copyAnimToLayoutParams() 根据当前的系统状态(如屏幕方向更改、窗口冻结和壁纸动作)来检查是否需要更新状态。
final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
&& root.copyAnimToLayoutParams();
//满足hasPendingLayoutChanges和doRequest之一,就需要重新请求布局
if (hasPendingLayoutChanges || doRequest) {
//requestTraversal()是属于WMS.relayoutWindow中的流程
mService.mWindowPlacerLocked.requestTraversal();
}
//检查根窗口是否正在进行动画
final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_ALL /* typesToCheck */);
//如果根窗口正在进行动画,并且之前没有进行过动画
if (rootAnimating && !mLastRootAnimating) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
//如果根窗口不再进行动画,并且之前正在进行动画
if (!rootAnimating && mLastRootAnimating) {
mService.mWindowPlacerLocked.requestTraversal();
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
//当前动画进行状态变为,上一次动画进行状态,以便下次使用
mLastRootAnimating = rootAnimating;
//判断TRANSITION 当前窗口 和 CHILDREN 当前窗口的子节点的
//ANIMATION_TYPE_APP_TRANSITION 应用切换、
//ANIMATION_TYPE_SCREEN_ROTATION 屏幕旋转和
//ANIMATION_TYPE_RECENTS 最近任务
//等这些动画类型是否正在进行
final boolean runningExpensiveAnimations =
root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
| ANIMATION_TYPE_RECENTS /* typesToCheck */);
//mRunningExpensiveAnimations,表示上一次的RunningExpensiveAnimations的值,
//如果runningExpensiveAnimations为true,
//并且mRunningExpensiveAnimations为false,
//则暂停任务快照的持久化并设置早期的唤醒开始
if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
// Usually app transitions put quite a load onto the system already (with all the things
// happening in app), so pause task snapshot persisting to not increase the load.
mService.mTaskSnapshotController.setPersisterPaused(true);
mTransaction.setEarlyWakeupStart();
//如果runningExpensiveAnimations为false,
//并且mRunningExpensiveAnimations为true,
//则恢复任务快照的持久化和设置早期的唤醒结束
} else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
mService.mTaskSnapshotController.setPersisterPaused(false);
mTransaction.setEarlyWakeupEnd();
}
//把当前的runningExpensiveAnimations赋值给mRunningExpensiveAnimations
//以便下次使用
mRunningExpensiveAnimations = runningExpensiveAnimations;
//将当前事务合并到全局事务中
SurfaceControl.mergeToGlobalTransaction(mTransaction);
//关闭当前的事务
mService.closeSurfaceTransaction("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
//如果标记为需要移除被替换的窗口
//则从根窗口容器中移除这些窗口,并重置该标记
if (mRemoveReplacedWindows) {
root.removeReplacedWindows();
mRemoveReplacedWindows = false;
}
//分发和处理待处理事件的作用。
//这个方法根据事件的类型(如任务出现、任务消失、任务信息改变等)来调用相应的处理方法。
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
//执行在PrepareSurfaces后需要执行的一些任务
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
动画移除流程
动画播放结束后回调函数分析
回调函数的调用过程
在动画播放流程中,SurfaceAnimationRunner.startAnimationLocked方法里,对动画结束通过onAnimationEnd
方法进行了监听。
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
//从mRunningAnimations这个映射(或字典)中移除与a.mLeash相关的动画。
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);
}
}
}
}
mRunningAnimations.remove(a.mLeash);
移除动画,后续mAnimationThreadHandler.post(a.mFinishCallback);
回调函数。
其中传递的参数a.mFinishCallback
,在RunningAnimation构造方法中对mFinishCallback
进行了赋值。
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
mAnimSpec = animSpec;
mLeash = leash;
mFinishCallback = finishCallback;
}
就是SurfaceAnimationRunner.startAnimation方法中的
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, finishCallback);
其中的finishCallback
是从前面LocalAnimationAdapter.startAnimation方法中传递的() -> finishCallback.onAnimationFinished(type, this)
,调用这个函数开始动画的结束流程。
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
前面说了动画的播放流程,这里我们主要关注移除流程() -> finishCallback.onAnimationFinished(type, this)
。
即a.mFinishCallback
指的就是() -> finishCallback.onAnimationFinished(type, this)
回调函数的实现
onAnimationFinished(type, this)
是OnAnimationFinishedCallback接口中的方法。
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
/**
* Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
* component that is running the animation when the animation is finished.
*/
interface OnAnimationFinishedCallback {
void onAnimationFinished(@AnimationType int type, AnimationAdapter anim);
}
这是一个函数式接口,我们来看看这个接口是怎么实现的
finishCallback
是OnAnimationFinishedCallback接口对象。结合前面SurfaceAnimator.startAnimation方法中调用的
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
即finishCallback
的值就是mInnerAnimationFinishedCallback
,这个参数同样是在SurfaceAnimator的构造方法中初始化的。
/**
* @param animatable The object to animate.
* @param staticAnimationFinishedCallback Callback to invoke when an animation has finished
* running.
*/
SurfaceAnimator(Animatable animatable,
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
-
先来看看
staticAnimationFinishedCallback
前面说过在WindowContainer构造方法中初始化
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
从代码中看staticAnimationFinishedCallback
传递的是this::onAnimationFinished
,onAnimationFinished在WindowContainer中实现
代码路径frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java/** * Called when an animation has finished running. */ protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) { ...... }
staticAnimationFinishedCallback.onAnimationFinished
其实就是调用WindowContainer.onAnimationFinished方法 -
再来看看
mInnerAnimationFinishedCallback
其值是getFinishedCallback(staticAnimationFinishedCallback)
,即finishCallback
实际上就是getFinishedCallback(staticAnimationFinishedCallback)
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.javaprivate OnAnimationFinishedCallback getFinishedCallback( @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) { return (type, anim) -> { ...... }; }
getFinishedCallback方法中实现了onAnimationFinished方法
() -> finishCallback.onAnimationFinished(type, this)
中onAnimationFinished(type, this)
方法的实现,就是对应的是SurfaceAnimator.getFinishedCallback方法中的(type, anim) -> {......}
。
从上述代码中我们发现,如果回调参数的值是从WindowContainer传递过来的,那么就是是调用的WindowContainer.onAnimationFinished方法(如:this::onAnimationFinished
对应的变量staticAnimationFinishedCallback
调用的onAnimationFinished方法);
而在其他情况调用的onAnimationFinished方法,是调用SurfaceAnimator.getFinishedCallback方法中的(type, anim) -> {......}
。
下面我们来看看SurfaceAnimator.getFinishedCallback方法。
回调处理动画完成的逻辑
SurfaceAnimator.getFinishedCallback()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
private OnAnimationFinishedCallback getFinishedCallback(
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {
return (type, anim) -> {
synchronized (mService.mGlobalLock) {
//移除AnimationAdapter对应的SurfaceAnimator,并将这个SurfaceAnimator返回给target
//mAnimationTransferMap属于启动窗口的动画场景,这里我们不涉及
final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
if (target != null) {
//递归调用onAnimationFinished(type, anim),即return (type, anim) -> {......}
//直到所有的SurfaceAnimator移除完
target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);
return;
}
//检查动画是否已被新动画替换,如果当前动画 (anim) 不等于之前存储的动画 (mAnimation),则不执行后续操作
if (anim != mAnimation) {
return;
}
//定义一个名为 resetAndInvokeFinish 的 Runnable
final Runnable resetAndInvokeFinish = () -> {
// We need to check again if the animation has been replaced with a new
// animation because the animatable may defer to finish.
//再次检查动画是否已被新动画替换,因为可设置动画可能会延迟到完成。
if (anim != mAnimation) {
return;
}
//mSurfaceAnimationFinishedCallback是在WindowContainer.startAnimation中赋值的
//其传递值为null,最终SurfaceAnimator.startAnimation赋值给mSurfaceAnimationFinishedCallback
final OnAnimationFinishedCallback animationFinishCallback =
mSurfaceAnimationFinishedCallback;
//重置与动画相关的状态
reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
//WindowContainer构造方法中给SurfaceAnimator构造方法传递了staticAnimationFinishedCallback
if (staticAnimationFinishedCallback != null) {
//当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。
//这是一个静态回调,它对通过这个 SurfaceAnimator 启动的所有动画都有效。
//回调WindowContainer.onAnimationFinished方法
staticAnimationFinishedCallback.onAnimationFinished(type, anim);
}
//mSurfaceAnimationFinishedCallback的值为null,因此animationFinishCallback的值为null
if (animationFinishCallback != null) {
//当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。
//这个回调是每个动画(即每个 AnimationAdapter)特有的。
//如果在WindowContainer.startAnimation方法中有赋值,
//则回调WindowContainer.onAnimationFinished方法
animationFinishCallback.onAnimationFinished(type, anim);
}
};
// If both the Animatable and AnimationAdapter requests to be deferred, only the
// first one will be called.
//如果 mAnimatable 或动画本身请求延迟动画完成,并且它们都没有被延迟,
//那么直接执行 resetAndInvokeFinish.run()。否则,延迟执行。
if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
resetAndInvokeFinish.run();
}
//设置动画完成标志,将 mAnimationFinished 设置为 true
mAnimationFinished = true;
}
};
}
这个方法主要做了这几件事:
- 通过递归的方式移除所有AnimationAdapter对应的SurfaceAnimator
其中的mAnimationTransferMap在启动窗口流程中,ActivityRecord.addStartingWindow中有调用transferStartingWindow方法,逐步调用到SurfaceAnimator.transferAnimation中进行添加mService.mAnimationTransferMap.put(mAnimation, this);
,这里我们不涉及,因此target的值为null - 使用
shouldDeferAnimationFinish
方法(默认返回false)用来判断是否需要延迟完成动画 - 执行
resetAndInvokeFinish.run()
,调用reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
重置动画相关状态 - 最后调用回调通过
staticAnimationFinishedCallback.onAnimationFinished(type, anim);
,调用WindowContainer.onAnimationFinished方法处理和响应动画完成的逻辑
重置动画相关状态并移除leash
reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
private void reset(Transaction t, boolean destroyLeash) {
//移除AnimationAdapter对应的SurfaceAnimator
mService.mAnimationTransferMap.remove(mAnimation);
mAnimation = null;
mSurfaceAnimationFinishedCallback = null;
//动画类型置为空
mAnimationType = ANIMATION_TYPE_NONE;
//屏幕冻结时的快照
final SurfaceFreezer.Snapshot snapshot = mSnapshot;
mSnapshot = null;
if (snapshot != null) {
// Reset the mSnapshot reference before calling the callback to prevent circular reset.
//如果有屏幕冻结时的快照,取消该动画。
//最终会调用到SurfaceAnimationRunner.onAnimationCancelled
snapshot.cancelAnimation(t, !destroyLeash);
}
if (mLeash == null) {
return;
}
//使用leash存储动画图层mLeash
SurfaceControl leash = mLeash;
//把动画图层置为空
mLeash = null;
//移除leash
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
//将mAnimationFinished设置为false
mAnimationFinished = false;
if (scheduleAnim) {
//leash成功移除后,在WMS中会通过WindowAnimator调度动画,协调各个窗口
mService.scheduleAnimationLocked();
}
}
移除leash
调用removeLeash方法移除leash
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
传递的mAnimatable
表示当前窗口,leash
就是动画图层,destroyLeash
在前面getFinishedCallback流程中传递的值为true
static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
boolean destroy) {
/* log add start*/
Slog.i("WindowManager:","removeLeash leash = " + leash , new Exception());
/* log add end*/
//scheduleAnim一个标志位,初始值为false
//为true时,走前面reset方法中的mService.scheduleAnimationLocked()流程
boolean scheduleAnim = false;
//获取当前窗口的SurfaceControl
final SurfaceControl surface = animatable.getSurfaceControl();
//获取当前窗口的父窗口的SurfaceControl
final SurfaceControl parent = animatable.getParentSurfaceControl();
//获取动画图层
final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
// If the animation leash is set, and it is different from the removing leash, it means the
// surface now has a new animation surface. We don't want to reparent for that.
//1.surface不为空
//2.curAnimationLeash不为空,且curAnimationLeash等于leash
//因此reparent值为true
final boolean reparent = surface != null && (curAnimationLeash == null
|| curAnimationLeash.equals(leash));
if (reparent) {
ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to original parent: %s for %s",
parent, animatable);
// We shouldn't really need these isValid checks but we do
// b/130364451
//判断当前窗口的surface是否有效,以及该窗口的父窗口的图层不为空且有效
if (surface.isValid() && parent != null && parent.isValid()) {
//把当前窗口图层和其父窗口的图层重新建立父子关系
t.reparent(surface, parent);
//scheduleAnim置为true
scheduleAnim = true;
}
}
//destroy传递过来值为true
if (destroy) {
//移除图层
t.remove(leash);
//scheduleAnim置为true
scheduleAnim = true;
}
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
// wasn't possible, but we still need to invoke the callback)
//1.移除和leash相关联的窗口和surface(这个只在前面requiresEdgeExtension为true时逻辑中有涉及)
//2.调整surface
animatable.onAnimationLeashLost(t);
//scheduleAnim置为true
scheduleAnim = true;
}
return scheduleAnim;
}
-
获取当前窗口的图层
final SurfaceControl surface = animatable.getSurfaceControl();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java/** * @return The SurfaceControl for this container. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getSurfaceControl() { return mSurfaceControl; }
直接返回一个SurfaceControl。
-
获取当前窗口父窗口的图层
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java/* * @return The SurfaceControl parent for this containers SurfaceControl. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getParentSurfaceControl() { final WindowContainer parent = getParent(); if (parent == null) { return null; } return parent.getSurfaceControl(); } @Override final protected WindowContainer getParent() { return mParent; }
先获取当前窗口的父窗口,在获取父窗口的SurfaceControl。
-
获取动画图层
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java@Override public SurfaceControl getAnimationLeash() { return mAnimationLeash; }
mAnimationLeash
是前面SurfaceAnimator的startAnimation方法中的mAnimatable.onAnimationLeashCreated(t, mLeash);
,把mLeash
赋值给了mAnimationLeash
,因此这个方法获取的是动画图层。 -
当前窗口图层和其父窗口的图层重新建立父子关系
t.reparent(surface, parent);
test-window的SurfaceControl重新认WindowToken的SurfaceControl为父。
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java/** * Re-parents a given layer to a new parent. Children inherit transform (position, scaling) * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the * parent Surface. * * @param sc The SurfaceControl to reparent * @param newParent The new parent for the given control. * @return This Transaction */ @NonNull public Transaction reparent(@NonNull SurfaceControl sc, @Nullable SurfaceControl newParent) { //检查传入的SurfaceControl对象是否满足某些预设条件 checkPreconditions(sc); long otherObject = 0; if (newParent != null) { //检查新父对象是否被释放。如果已经被释放,那么它会抛出异常。 newParent.checkNotReleased(); //新父对象不为null且未被释放,那么将新父对象的Native对象赋值给otherObject。 otherObject = newParent.mNativeObject; } //传入了三个参数:1.当前对象的Native对象 2.被重新设置父对象的SurfaceControl的Native对象 3.新父对象的Native对象。 //用于实现重新设置父对象的具体操作。 nativeReparent(mNativeObject, sc.mNativeObject, otherObject); //把被重新设置父对象的SurfaceControl和新父对象存储到mReparentedSurfaces这个map中。 mReparentedSurfaces.put(sc, newParent); return this; }
前面说过
reparent
方法中通过mReparentedSurfaces
这个ArrayMap临时存储父子关系,key值存储SurfaceControl对象,value为其父SurfaceControl对象,此时mReparentedSurfaces
的键值对为:
key:Surface(name=8aabddb test-window)/@0x1038e9a
(当前窗口的SurfaceControl)
value:Surface(name=WindowToken{15f96ea type=2038 android.os.BinderProxy@1d0fed5})/@0xc7312c1
(当前窗口的父窗口的SurfaceControl,即WindowToken的SurfaceControl)
此时leash还没有被释放,WindowToken的SurfaceControl有两个儿子SurfaceControl,关系如下图所示:
此时leash逐渐发现不对劲,但是假装不知道
假如我们后面不执行移除leash图层的操作,那么这个图层一直会保持这个状态挂在WindowToken上和test-window共享父亲。 -
移除动画图层
t.remove(leash);
/** * Equivalent to reparent with a null parent, in that it removes * the SurfaceControl from the scene, but it also releases * the local resources (by calling {@link SurfaceControl#release}) * after this method returns, {@link SurfaceControl#isValid} will return * false for the argument. * * @param sc The surface to remove and release. * @return This transaction * @hide */ @NonNull public Transaction remove(@NonNull SurfaceControl sc) { reparent(sc, null); sc.release(); return this; }
同样调用了
reparent
方法,先把需要remove的图层的父图层置空,然后释放。
此时mReparentedSurfaces
的键值对为:
key:Surface(name=8aabddb test-window)/@0x1038e9a
(当前窗口的SurfaceControl)
value:Surface(name=WindowToken{15f96ea type=2038 android.os.BinderProxy@1d0fed5})/@0xc7312c1
(WindowToken的SurfaceControl)
和
key:Surface(name=Surface(name=8aabddb test-window)/@0x1038e9a - animation-leash of window_animation)/@0x38e37a8
(动画图层leash)
value:null
过程如下所示:
leash:原来我才是多余的那个,悠悠苍天,何薄于我! -
移除和leash相关联的窗口和surface并调整surface
animatable.onAnimationLeashLost(t);
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java@Override public void onAnimationLeashLost(Transaction t) { mLastLayer = -1; //调用mWmService中的mSurfaceAnimationRunner对象的onAnimationLeashLost方法 //用于移除和leash相关联的窗口,这个只在前面requiresEdgeExtension为true时逻辑中有涉及 mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t); //mAnimationLeash置为空 mAnimationLeash = null; mNeedsZBoost = false; //调整其所有child的z-order reassignLayer(t); //更新Surface位置 updateSurfacePosition(t); }
其中
mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);
的mAnimationLeash
前面说过就是动画图层。这个只在前面SurfaceAnimationRunner的startAnimation方法中requiresEdgeExtension
为true
时逻辑中有涉及,其为true时才会操作mEdgeExtensions
这个ArrayList,这里不讨论。
协调动画显示
在SurfaceAnimator.reset()方法最后调用了mService.scheduleAnimationLocked();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void scheduleAnimationLocked() {
mAnimator.scheduleAnimation();
}
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
//mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排
mAnimationFrameCallbackScheduled = true;
//每一帧被绘制时,回调mAnimationFrameCallback
mChoreographer.postFrameCallback(mAnimationFrameCallback);
}
}
和前面显示动画的流程一样有调用WindowAnimator的scheduleAnimation()方法,协调动画和窗口的显示。
处理和响应动画完成的逻辑
WindowContainer.onAnimationFinished方法
代码路径frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
/**
* Called when an animation has finished running.
*/
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
//主要用于 清空 mSurfaceAnimationSources 列表
doAnimationFinished(type, anim);
//WindowManagerService中实现onAnimationFinished()
//用于唤醒所有等待mGlobalLock对象的线程,确保多个线程能够正确地执行任务
mWmService.onAnimationFinished();
//将 mNeedsZBoost 设置为 false,表示不再需要Z轴增强
mNeedsZBoost = false;
}
private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {
//mSurfaceAnimationSources中每个容器,做对应的onAnimationFinished
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
}
//清除动画源列表
mSurfaceAnimationSources.clear();
if (mDisplayContent != null) {
//调用DisplayContent的onWindowAnimationFinished方法
//从当前源码上看,主要是针对输入法相关做了一些操作
mDisplayContent.onWindowAnimationFinished(this, type);
}
}
mSurfaceAnimationSources
的值是在applyAnimationUnchecked方法中添加的,我们这里不属于远程动画流程,不涉及。
在ActivityRecord和WindowState中都重写了这个方法。我们这里是窗口动画,所以看看WindowState中的逻辑。
@Override
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
super.onAnimationFinished(type, anim);
mWinAnimator.onAnimationFinished();
}
先调用了父类前面WindowContainer中的onAnimationFinished,然后调用了WindowStateAnimator的onAnimationFinished方法。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
void onAnimationFinished() {
// Done animating, clean up.
ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s: exiting=%b, reportedVisible=%b",
this, mWin.mAnimatingExit,
(mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
//检查窗口可见性变化的flag
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
//判断窗口类型为状态栏或通知栏的所有可见性标志位是否为true
if ((mAttrType == LayoutParams.TYPE_STATUS_BAR
|| mAttrType == LayoutParams.TYPE_NOTIFICATION_SHADE) && mWin.isVisibleByPolicy()) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
//设置mLayoutNeeded标志位为true
displayContent.setLayoutNeeded();
}
//处理动画结束后窗口及其子窗口的状态更新和资源清理
mWin.onExitAnimationDone();
//将 displayContent 的 pendingLayoutChanges 标志设置为 FINISH_LAYOUT_REDO_ANIM
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
//如果窗口是壁纸的目标
if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
//将 pendingLayoutChanges 标志设置为 FINISH_LAYOUT_REDO_WALLPAPER
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
"WindowStateAnimator", displayContent.pendingLayoutChanges);
}
if (mWin.mActivityRecord != null) {
//更新窗口对应的应用的可见性
mWin.mActivityRecord.updateReportedVisibilityLocked();
}
}
总结
至此动画从添加到移除的逻辑梳理完成,我们可以发现简单概括就起来动画的显示流程就是:
在窗口添加或移除时,添加动画,新增leash图层在当前窗口和其对应WindowToken之间,调整显示层级关系;播放完动画后,移除动画,移除leash图层,再次调整显示层级关系。
其他
SurfaceControl
Surface和SurfaceControl的关系是,Surface是图形生产者和图像消费之间传递数据的缓冲区,而SurfaceControl是Surface的控制类,可以实现复杂的Surface操作,如改变位置、缩放、剪切、透明度以及Z序等。所以当我们需要对某个Surface操作时,只需要修改其SurfaceControl即可。
代码位置:frameworks/base/core/java/android/view/SurfaceControl.java
Surface相关的控制方法都在这里
创建图层
SurfaceControl.Builder的build()方法用于创建图层
/**
* Builder class for {@link SurfaceControl} objects.
*
* By default the surface will be hidden, and have "unset" bounds, meaning it can
* be as large as the bounds of its parent if a buffer or child so requires.
*
* It is necessary to set at least a name via {@link Builder#setName}
*/
public static class Builder {
private SurfaceSession mSession;
private int mFlags = HIDDEN;
private int mWidth;
private int mHeight;
private int mFormat = PixelFormat.OPAQUE;
private String mName;
private WeakReference<View> mLocalOwnerView;
private SurfaceControl mParent;
private SparseIntArray mMetadata;
private String mCallsite = "SurfaceControl.Builder";
/**
* Begin building a SurfaceControl with a given {@link SurfaceSession}.
*
* @param session The {@link SurfaceSession} with which to eventually construct the surface.
* @hide
*/
public Builder(SurfaceSession session) {
mSession = session;
}
/**
* Begin building a SurfaceControl.
*/
public Builder() {
}
/**
* Construct a new {@link SurfaceControl} with the set parameters. The builder
* remains valid.
*/
@NonNull
public SurfaceControl build() {
if (mWidth < 0 || mHeight < 0) {
throw new IllegalStateException(
"width and height must be positive or unset");
}
if ((mWidth > 0 || mHeight > 0) && (isEffectLayer() || isContainerLayer())) {
throw new IllegalStateException(
"Only buffer layers can set a valid buffer size.");
}
if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
setBLASTLayer();
}
return new SurfaceControl(
mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
mLocalOwnerView, mCallsite);
}
......
}
移除图层
SurfaceControl.Transaction的remove()方法用于移除图层
/**
* An atomic set of changes to a set of SurfaceControl.
*/
public static class Transaction implements Closeable, Parcelable {
......
/**
* Equivalent to reparent with a null parent, in that it removes
* the SurfaceControl from the scene, but it also releases
* the local resources (by calling {@link SurfaceControl#release})
* after this method returns, {@link SurfaceControl#isValid} will return
* false for the argument.
*
* @param sc The surface to remove and release.
* @return This transaction
* @hide
*/
@NonNull
public Transaction remove(@NonNull SurfaceControl sc) {
reparent(sc, null);
sc.release();
return this;
}
......
}
修改图层父子关系
SurfaceControl.Transaction的reparent()方法用于修改图层父子关系
/**
* Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
* parent Surface.
*
* @param sc The SurfaceControl to reparent
* @param newParent The new parent for the given control.
* @return This Transaction
*/
@NonNull
public Transaction reparent(@NonNull SurfaceControl sc,
@Nullable SurfaceControl newParent) {
//检查传入的SurfaceControl对象是否满足某些预设条件
checkPreconditions(sc);
long otherObject = 0;
if (newParent != null) {
//检查新父对象是否被释放。如果已经被释放,那么它会抛出异常。
newParent.checkNotReleased();
//新父对象不为null且未被释放,那么将新父对象的Native对象赋值给otherObject。
otherObject = newParent.mNativeObject;
}
//传入了三个参数:1.当前对象的Native对象 2.被重新设置父对象的SurfaceControl的Native对象 3.新父对象的Native对象。
//用于实现重新设置父对象的具体操作。
nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
//把被重新设置父对象的SurfaceControl和新父对象存储到mReparentedSurfaces这个map中。
mReparentedSurfaces.put(sc, newParent);
return this;
}
简单聊聊Choreographer
Choreographer扮演 Android 渲染链路中承上启下的角色
承上:负责接收和处理 App 的各种更新消息和回调,等到 Vsync(垂直同步) 到来的时候统一处理。比如集中处理 Input(主要是 Input 事件的处理) 、Animation(动画相关)、Traversal(包括 measure、layout、draw 等操作) ,判断卡顿掉帧情况,记录 CallBack 耗时等
启下:负责请求和接收 Vsync 信号。接收 Vsync 事件回调(通过 FrameDisplayEventReceiver.onVsync );请求 Vsync(FrameDisplayEventReceiver.scheduleVsync)
我们这里mChoreographer.postFrameCallback(this::startAnimations);
其实就是先请求Vsync信号,然后接收到Vsync信号回调this::startAnimations
。
Choreographer的postFrameCallback和postCallback在处理方式和使用场景上有所不同。
Choreographer的postFrameCallback主要用于在下一个frame渲染时控制执行某些操作,例如View的绘制流程。这个方法会在下一个Vsync信号来临时,遍历CallbackQuenes中的链表,并执行对应的回调方法。这个方法主要在ViewRootImpl中使用,用于分发处理NativeInputQueue的消息回调。
而Choreographer的postCallback则是一个更通用的回调方法,它允许在UI线程中安排一个回调。这个方法可以接受Runnable对象作为参数,该对象定义了回调时要执行的任务。postCallback方法可以在任何需要延迟执行任务或异步处理任务的情况下使用,而不仅仅是在渲染帧时。
总的来说,postFrameCallback更专注于在特定时间点(如渲染帧时)执行特定任务,而postCallback则提供了一种更通用的方式来安排回调,可以在任何需要的时候使用。