序
如何创建一个窗口动画?我们通过先从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>
创建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方法中添加堆栈来查看其调用流程
android.util.Log.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方法中添加堆栈来查看其调用流程
android.util.Log.i("WindowManager:","removeLeash leash = "+leash.toString(),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
动画移除
动画播放完成,去掉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;
}
进入performShowLocked()流程后mDrawState更新HAS_DRAWN。
mWinAnimator.applyEnterAnimationLocked();
进入动画流程
确定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;
if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
if (anim != DisplayPolicy.ANIMATION_NONE) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#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);
}
selectAnimation
根据mWin(当前窗口)和transit的值来选择显示的动画,之后返回给anim变量
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
判断我们的窗口是否是StatusBar、NavigationBar
win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt || win == mExtraNavBarAlt
其他的一些特定窗口,这些特定的窗口对象可能是代表不同的屏幕区域或组件,例如备用的状态栏、备用的导航栏、气候栏和额外的导航栏。
transit == TRANSIT_PREVIEW_DONE
窗口预览动画已经完成的状态为true
我们这里都不符合这些条件,因此直接返回ANIMATION_STYLEABLE;
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
。即使应用侧没有定义android:windowEnterAnimation
这类属性,也不影响attr
的值。添加窗口没用动画attr
的值仍然为0x0
,移除窗口也是同理。后面调用loadAnimationAttr方法时会用到
loadAnimationAttr
mWin.getDisplayContent().mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_OLD_NONE);
mAppTransition是AppTransition对象,这个类是用于管理应用程序过渡动画的类
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对象。
startAnimation
mWin.startAnimation(a);
用于启动动画,其中参数a
为前面调用loadAnimationAttr方法后的值,即应用侧传递的动画。具体流程看后续的WindowState.startAnimation(),初始化动画参数并通过AnimationAdapter保存动画
isAnimating
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,则说明不存在符合条件的动画。
初始化动画参数并通过AnimationAdapter保存动画
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()方法安排或触发动画的执行
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对象的属性来创建和执行动画。
传递启动动画需要的参数
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方法
-
入参含义
Transaction
:这是一个对象,用于描述一系列的窗口操作,例如移动、调整大小、绘制等。这些操作在WMS中排队,并在适当的时机应用到窗口上。
AnimationAdapter
:这是对动画进行封装的类。它包含了一些关于如何开始、更新和结束动画的信息。
hidden
:这个布尔值表示窗口是否隐藏。如果窗口是隐藏的,那么就不会显示动画。
type
:这个整数代表了动画的类型。这里我们传递的是ANIMATION_TYPE_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并通过AnimationAdapter启动动画
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*/
android.util.Log.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。
即此时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);
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
。
之后,调用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传给AnimationAdapter,执行动画
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
mAnimation
是AnimationAdapter接口的对象,调用其startAnimation
方法,传递mLeash(动画)、t(事务)、type(动画类型)和mInnerAnimationFinishedCallback(回调函数),这个接方法是在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);
-
() -> finishCallback.onAnimationFinished(type, this))
当动画完成时,这个函数会被调用,并执行finishCallback.onAnimationFinished(type, this)
这里LocalAnimationAdapter.startAnimation方法的作用实际上就是播放完动画后,通过回调函数移除动画,这里我们关注播放流程。
通过SurfaceAnimationRunner启动动画
代码路径: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()
方法则用于恢复动画的启动。当这个方法被调用时,动画的启动不再被推迟,所有待处理的动画会开始播放。
这两个方法通常用于动画的复杂控制,例如,在数据加载或其他后台任务完成之前,你可能希望延迟动画的播放,直到这些任务完成。
简单来说,这两个方法的主要作用是控制何时开始播放动画。
通过请求和接收 Vsync 信号播放动画
mChoreographer.postFrameCallback(this::startAnimations);
通过Choreographer的postFrameCallback
方法在下一帧绘制完成后回调this::startAnimations
实际调用的就是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) {
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
anim.overrideDurationScale(1.0f);
anim.setDuration(a.mAnimSpec.getDuration());
anim.addUpdateListener(animation -> {
synchronized (mCancelLock) {
if (!a.mCancelled) {
final long duration = anim.getDuration();
long currentPlayTime = anim.getCurrentPlayTime();
if (currentPlayTime > duration) {
currentPlayTime = duration;
}
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// Transaction will be applied in the commit phase.
scheduleApplyTransaction();
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
synchronized (mCancelLock) {
if (!a.mCancelled) {
// TODO: change this back to use show instead of alpha when b/138459974 is
// fixed.
mFrameTransaction.setAlpha(a.mLeash, 1);
}
}
}
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
mRunningAnimations.remove(a.mLeash);
synchronized (mCancelLock) {
if (!a.mCancelled) {
// Post on other thread that we can push final state without jank.
mAnimationThreadHandler.post(a.mFinishCallback);
}
}
}
}
});
a.mAnim = anim;
mRunningAnimations.put(a.mLeash, a);
anim.start();
if (a.mAnimSpec.canSkipFirstFrame()) {
// If we can skip the first frame, we start one frame later.
anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
}
// 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());
}
简单聊聊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
。
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
动画移除流程
回到前面说的mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
@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)
先来看看finishCallback
也就是传递过来的mInnerAnimationFinishedCallback
,这个参数同样是在SurfaceAnimator的构造方法中初始化的。
SurfaceAnimator(Animatable animatable,
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
调用getFinishedCallback(staticAnimationFinishedCallback);
方法初始化mInnerAnimationFinishedCallback
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;
}
......
}