本文介绍安卓动画系统中的转屏动画流程。

1 旋转的开始

若当前屏幕方向需要旋转,会调用frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateRotation方法:

    /*** Recalculate the current rotation.** Called by the window manager policy whenever the state of the system changes* such that the current rotation might need to be updated, such as when the* device is docked or rotated into a new posture.*/@Overridepublic void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);}private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"+ " alwaysSendConfiguration=%b forceRelayout=%b",alwaysSendConfiguration, forceRelayout);Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");long origId = Binder.clearCallingIdentity();try {synchronized (mGlobalLock) {boolean layoutNeeded = false;final int displayCount = mRoot.mChildren.size();for (int i = 0; i < displayCount; ++i) {final DisplayContent displayContent = mRoot.mChildren.get(i);Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");final boolean rotationChanged = displayContent.updateRotationUnchecked();Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);if (rotationChanged) {mAtmService.getTaskChangeNotificationController().notifyOnActivityRotation(displayContent.mDisplayId);}if (!rotationChanged || forceRelayout) {displayContent.setLayoutNeeded();layoutNeeded = true;}if (rotationChanged || alwaysSendConfiguration) {displayContent.sendNewConfiguration();}}if (layoutNeeded) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,"updateRotation: performSurfacePlacement");mWindowPlacerLocked.performSurfacePlacement();Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}} finally {Binder.restoreCallingIdentity(origId);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}

遍历在mRoot.mChildren中的每个DisplayContent(frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java),首先调用了其updateRotationUnchecked方法:

    /*** Update rotation of the display.** @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL*         {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN.*/boolean updateRotationUnchecked() {return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);}

这里直接调用了frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java的updateRotationUnchecked方法:

    /*** Update rotation with an option to force the update. This updates the container's perception* of rotation and, depending on the top activities, will freeze the screen or start seamless* rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}* during {@link DisplayContent#sendNewConfiguration}.** @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating*                    orientation because we're waiting for some rotation to finish or display*                    to unfreeze, which results in configuration of the previously visible*                    activity being applied to a newly visible one. Forcing the rotation*                    update allows to workaround this issue.* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL*         {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE*         THE SCREEN.*/boolean updateRotationUnchecked(boolean forceUpdate) {......mDisplayContent.setLayoutNeeded();if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {// The screen rotation animation uses a screenshot to freeze the screen while windows// resize underneath. When we are rotating seamlessly, we allow the elements to// transition to their rotated state independently and without a freeze required.prepareSeamlessRotation();} else {prepareNormalRotationAnimation();}// Give a remote handler (system ui) some time to reposition things.startRemoteRotation(oldRotation, mRotation);return true;}

这里通过DisplayRotation的shouldRotateSeamlessly方法判断是否可以无缝旋转:

    @VisibleForTestingboolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {// Display doesn't need to be frozen because application has been started in correct// rotation already, so the rest of the windows can use seamless rotation.if (mDisplayContent.hasTopFixedRotationLaunchingApp()) {return true;}final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();if (w == null || w != mDisplayContent.mCurrentFocus) {return false;}// We only enable seamless rotation if the top window has requested it and is in the// fullscreen opaque state. Seamless rotation requires freezing various Surface states and// won't work well with animations, so we disable it in the animation case for now.if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {return false;}// For the upside down rotation we don't rotate seamlessly as the navigation bar moves// position. Note most apps (using orientation:sensor or user as opposed to fullSensor)// will not enter the reverse portrait orientation, so actually the orientation won't change// at all.if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {return false;}// If the navigation bar can't change sides, then it will jump when we change orientations// and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation// where the navbar is low-profile enough that this isn't very noticeable.if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {return false;}// If the bounds of activity window is different from its parent, then reject to be seamless// because the window position may change after rotation that will look like a sudden jump.if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {return false;}// In the presence of the PINNED stack or System Alert windows we unfortunately can not// seamlessly rotate.if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask()|| mDisplayContent.hasAlertWindowSurfaces()) {return false;}// We can't rotate (seamlessly or not) while waiting for the last seamless rotation to// complete (that is, waiting for windows to redraw). It's tempting to check// mSeamlessRotationCount but that could be incorrect in the case of window-removal.if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {return false;}return true;}

2 旋转方法

2.1 无缝旋转

若能无缝旋转,则调用了DisplayRotation的prepareSeamlessRotation方法:

    private void prepareSeamlessRotation() {// We are careful to reset this in case a window was removed before it finished// seamless rotation.mSeamlessRotationCount = 0;mRotatingSeamlessly = true;}

2.2 冻屏旋转

若不能无缝旋转,则调用了DisplayRotation的prepareNormalRotationAnimation方法:

    void prepareNormalRotationAnimation() {cancelSeamlessRotation();final RotationAnimationPair anim = selectRotationAnimation();mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);}

在这里调用了frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的startFreezingDisplay方法进行冻屏:

    void startFreezingDisplay(int exitAnim, int enterAnim) {startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());}void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {startFreezingDisplay(exitAnim, enterAnim, displayContent,ROTATION_UNDEFINED /* overrideOriginalRotation */);}void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,int overrideOriginalRotation) {......displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,originalRotation));}

ScreenRotationAnimation在frameworks/base/services/core/java/com/android/server/wm/ScreenRotationAnimation.java中实现,其构造函数:

    ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {......// Check whether the current screen contains any secure content.final boolean isSecure = displayContent.hasSecureWindowOnScreen();final SurfaceControl.Transaction t = mService.mTransactionFactory.get();try {mBackColorSurface = displayContent.makeChildSurface(null).setName("BackColorSurface").setColorLayer().setCallsite("ScreenRotationAnimation").build();mScreenshotLayer = displayContent.makeOverlay().setName("RotationLayer").setBufferSize(mWidth, mHeight).setSecure(isSecure).setCallsite("ScreenRotationAnimation").build();mEnterBlackFrameLayer = displayContent.makeOverlay().setName("EnterBlackFrameLayer").setContainerLayer().setCallsite("ScreenRotationAnimation").build();// In case display bounds change, screenshot buffer and surface may mismatch so set a// scaling mode.SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);t2.apply(true /* sync */);// Capture a screenshot into the surface we just created.final int displayId = displayContent.getDisplayId();final Surface surface = mService.mSurfaceFactory.get();surface.copyFrom(mScreenshotLayer);SurfaceControl.ScreenshotGraphicBuffer gb =mService.mDisplayManagerInternal.systemScreenshot(displayId);if (gb != null) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,"ScreenRotationAnimation#getMedianBorderLuma");mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(),gb.getColorSpace());Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);try {surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),gb.getColorSpace());} catch (RuntimeException e) {Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());}// If the screenshot contains secure layers, we have to make sure the// screenshot surface we display it in also has FLAG_SECURE so that// the user can not screenshot secure layers via the screenshot surface.if (gb.containsSecureLayers()) {t.setSecure(mScreenshotLayer, true);}t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);t.reparent(mBackColorSurface, displayContent.getSurfaceControl());t.setLayer(mBackColorSurface, -1);t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});t.setAlpha(mBackColorSurface, 1);t.show(mScreenshotLayer);t.show(mBackColorSurface);} else {Slog.w(TAG, "Unable to take screenshot of display " + displayId);}surface.destroy();} catch (OutOfResourcesException e) {Slog.w(TAG, "Unable to allocate freeze surface", e);}ProtoLog.i(WM_SHOW_SURFACE_ALLOC,"  FREEZE %s: CREATE", mScreenshotLayer);setRotation(t, realOriginalRotation);t.apply();}

在这里对屏幕进行了截屏,保存在mScreenshotLayer中。

3 旋转动画

接下来回到WindowManagerService的updateRotation方法中,还调用了displayContent(frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java)的sendNewConfiguration方法:

    void sendNewConfiguration() {......// Something changed (E.g. device rotation), but no configuration update is needed.// E.g. changing device rotation by 180 degrees. Go ahead and perform surface placement to// unfreeze the display since we froze it when the rotation was updated in// DisplayContent#updateRotationUnchecked.if (mWaitingForConfig) {mWaitingForConfig = false;mWmService.mLastFinishedFreezeSource = "config-unchanged";setLayoutNeeded();mWmService.mWindowPlacerLocked.performSurfacePlacement();}}

mWmService.mWindowPlacerLocked是frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java,调用了其performSurfacePlacement方法进入窗口布局流程,会调用frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java的performSurfacePlacementNoTrace方法:

    // "Something has changed!  Let's make it correct now."// TODO: Super crazy long method that should be broken down...void performSurfacePlacementNoTrace() {......if (mOrientationChangeComplete) {if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);}mWmService.stopFreezingDisplayLocked();}......}

其中调用了WindowManagerService的stopFreezingDisplayLocked方法:

    void stopFreezingDisplayLocked() {......ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null: displayContent.getRotationAnimation();if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation");DisplayInfo displayInfo = displayContent.getDisplayInfo();// Get rotation animation again, with new top windowif (!displayContent.getDisplayRotation().validateRotationAnimation(mExitAnimId, mEnterAnimId, false /* forceDefault */)) {mExitAnimId = mEnterAnimId = 0;}if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {mTransaction.apply();} else {screenRotationAnimation.kill();displayContent.setRotationAnimation(null);updateRotation = true;}} else {if (screenRotationAnimation != null) {screenRotationAnimation.kill();displayContent.setRotationAnimation(null);}updateRotation = true;}......}

其中调用了ScreenRotationAnimation的dismiss方法:

    /*** Returns true if animating.*/public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {if (mScreenshotLayer == null) {// Can't do animation.return false;}if (!mStarted) {mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),mDisplayContent.getWindowingLayer());startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,exitAnim, enterAnim);if (mPerf != null && !mIsPerfLockAcquired) {mPerf.perfHint(BoostFramework.VENDOR_HINT_ROTATION_ANIM_BOOST, null);mIsPerfLockAcquired = true;}}if (!mStarted) {return false;}mFinishAnimReady = true;return true;}

又调用了ScreenRotationAnimation的startAnimation方法:

    /*** Returns true if animating.*/private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {......mStarted = true;// Figure out how the screen has moved from the original rotation.int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);final boolean customAnim;if (exitAnim != 0 && enterAnim != 0) {customAnim = true;mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_alpha);} else {customAnim = false;switch (delta) { /* Counter-Clockwise Rotations */case Surface.ROTATION_0:mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_0_exit);mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_0_enter);break;case Surface.ROTATION_90:mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_plus_90_exit);mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_plus_90_enter);break;case Surface.ROTATION_180:mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_180_exit);mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_180_enter);break;case Surface.ROTATION_270:mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_minus_90_exit);mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,R.anim.screen_rotate_minus_90_enter);break;}}......if (customAnim) {mSurfaceRotationAnimationController.startCustomAnimation();} else {mSurfaceRotationAnimationController.startScreenRotationAnimation();}return true;}

这里首先通过AnimationUtils的loadAnimation方法加载动画,然后调用了ScreenRotationAnimation的内部类SurfaceRotationAnimationController的startCustomAnimation或startScreenRotationAnimation方法:

        void startCustomAnimation() {try {mService.mSurfaceAnimationRunner.deferStartingAnimations();mRotateScreenAnimator = startScreenshotAlphaAnimation();mDisplayAnimator = startDisplayRotation();if (mEnteringBlackFrame != null) {mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();}} finally {mService.mSurfaceAnimationRunner.continueStartingAnimations();}}/*** Start the rotation animation of the display and the screenshot on the* {@link SurfaceAnimationRunner}.*/void startScreenRotationAnimation() {try {mService.mSurfaceAnimationRunner.deferStartingAnimations();mDisplayAnimator = startDisplayRotation();mScreenshotRotationAnimator = startScreenshotRotationAnimation();startColorAnimation();} finally {mService.mSurfaceAnimationRunner.continueStartingAnimations();}}

这里面有许多startXXXAnimation方法,它们都会调用SurfaceRotationAnimationController的startAnimation方法:

        /*** Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.** @param animatable The animatable used for the animation.* @param animationSpec The spec of the animation.* @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator}*                                    and called when the animation finishes.* @return The newly created {@link SurfaceAnimator} that as been started.*/private SurfaceAnimator startAnimation(SurfaceAnimator.Animatable animatable,LocalAnimationAdapter.AnimationSpec animationSpec,OnAnimationFinishedCallback animationFinishedCallback) {SurfaceAnimator animator = new SurfaceAnimator(animatable, animationFinishedCallback, mService);LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(animationSpec, mService.mSurfaceAnimationRunner);animator.startAnimation(mDisplayContent.getPendingTransaction(),localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION);return animator;}

在这里首先创建了一个frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java,然后调用了其startAnimation方法开始动画。

转屏动画 - 安卓R相关推荐

  1. 过渡动画 - 安卓R

    本文介绍安卓动画系统中的过渡动画流程. 过渡动画简介 一般过渡动画有在system_server进程中执行的本地动画和在例如systemui等进程执行的远程动画两种情况. 常见的本地动画:App内切换 ...

  2. Insets动画 - 安卓R

    Insets相关重要类 Insets Insets定义在frameworks/base/graphics/java/android/graphics/Insets.java,其分别定义了窗口上下左右有 ...

  3. 安卓动画系统 - 安卓R

    重要类介绍 Animation Animation定义在frameworks/base/core/java/android/view/animation/Animation.java,其子类有Tran ...

  4. Twitter的like动画安卓版 - 备选方案

    英文: Twitter's like animation in Android - alternative 相关动画网址:http://frogermcs.github.io/twitters-lik ...

  5. android 默认转场动画,安卓转场动画

    转场动画相关 一.系统自带动画效果 假如有 A 和 B 两个 Activity, 从 A 进入 B,调用 startActivity(Intent(this,AnimCutOutActivity::c ...

  6. android6.0关机动画,安卓系统修改开机动画,关机动画

    开机动画 两种开机动画 关于安卓动画的研究主要在frameworks/base/cmds/bootanimation/BootAnimation.cpp中. 安卓系统默认的开机动画(谷歌)的动画是显示 ...

  7. 【安卓R 源码】Ripple 水波纹效果源码

    安卓使用ripple实现点击时的涟漪效果 - 简书 https://www.jb51.net/article/145309.htm Android:RippleDrawable 水波纹/涟漪效果 - ...

  8. android 天气动画,安卓实现旋转风车动画(仿华为天气预报)

    旋转风车实现效果 实现方案 对于风车的立柱部分,采用一张固定背景图: 对于风车上面旋转部分,采用一张图,通过安卓属性动画进行旋转. 注意点: 因为是采用两张图叠加而形成的效果,所以需要注意调整两张图的 ...

  9. 窗口布局流程 - 安卓R

    窗口布局简介 在安卓的窗口管理系统中,经常要执行的一个操作是布局. 布局会做许多事情,例如更新焦点窗口.调整所有的窗口位置.设置wallpaperTarget以显示或隐藏壁纸.开始过渡动画等. 很多时 ...

最新文章

  1. 《可穿戴创意设计:技术与时尚的融合》一一第3章 从纺织品到可穿戴设备
  2. 计算机网络实验思考题汇总
  3. python序列类型映射类型_Python的数据访问模型与序列介绍
  4. c语言释放整个链表,最简单的链表删除第一个节点时释放内存的问题
  5. sap 供应商表_财务人员学习SAP的路线图
  6. cors 前后端分离跨域问题_前后端分离之CORS跨域访问踩坑总结
  7. Ubuntu下绘图软件krita64位无中文问题
  8. python哲学内容是_Python哲学(import this)
  9. pmp知识点详解-项目大牛整理_PMP项目管理第六章:项目进度管理(5)核心知识点...
  10. 记一次云服务器ssh爆破维护实例
  11. Telink zigbee射频和功耗测试的方法
  12. app store connect
  13. 【史玉柱自述:我的营销心得】-成功的牛人其实都有许多你不知道的技能
  14. 我的世界玩服务器虚拟内存高崩,服务端崩溃 - 崩溃解答 - MC百科社群 - MC百科|最大的Minecraft中文MOD百科...
  15. 霍学文:大数据重塑未来金融监管方式
  16. ardupilot java_基于Eclipse IDE的Ardupilot飞控源码阅读环境搭建
  17. 关闭打印机和无线服务器,打印机无线连接断开了怎么办?
  18. 靓丽图像中的一抹——摩尔纹
  19. js 微任务 宏任务的执行顺序解释
  20. 关于attach和detach的疑问

热门文章

  1. 分析游戏外挂样本的9大诀窍
  2. 怎样做网络推广浅析网站标题如何写更利于SEO?
  3. MVC5 + EF6 + Bootstrap3 (14) 分部视图PartialView
  4. Windows为什么越用越慢而Linux却不会?
  5. CreateProcessAsUser的用法
  6. 用MATLAB求解拟合公式系数和拟合优度
  7. plsql无法连接oracle,报错:ORA-12514
  8. 2022 百度面试题
  9. 5.10 自定义颜色至色板和全局色的使用 [Illustrator CC教程]
  10. 曾经开发的一个武冈市市民意见采集系统