了解AsyncRotationController

概述

基于android 15.0, 以从强制横屏App上滑退回桌面流程来分析

frameworks/base/services/core/java/com/android/server/wm/AsyncRotationController.java

AsyncRotationController 是一种控制器,用于处理设备显示屏旋转时非活动窗口的异步更新。这种控制器通过异步处理来优化屏幕旋转或应用过渡动画的启动延迟,确保窗口在旋转过程中能够平滑过渡,避免闪烁或延迟问题。具体功能包括:

  • 在旋转变化时处理窗口的淡出和淡入效果。
  • 隐藏和显示目标窗口以匹配新的旋转角度。
  • 使用同步事务管理无缝旋转,确保窗口能够平滑过渡到新的旋转状态。
Async Rotation执行时机

在这里插入图片描述

// DisplayContent.java
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {if (mFixedRotationLaunchingApp == null && r != null) {mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);// 延迟隐藏动画,以避免在短时间内点击导航栏可能触发固定旋转时出现闪烁final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents|| mTransitionController.isTransientLaunch(r);startAsyncRotation(shouldDebounce);} else if (mFixedRotationLaunchingApp != null && r == null) {mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);// 如果请求display的下一次transition,保持异步旋转控制器。if (!mTransitionController.hasCollectingRotationChange(this, getRotation())) {finishAsyncRotationIfPossible();}}mFixedRotationLaunchingApp = r;
}

用于启动异步旋转过程。这个过程允许应用程序或系统在不阻塞主线程的情况下处理显示屏的旋转,从而提供更平滑的用户体验.

// DisplayContent.java
private boolean startAsyncRotation(boolean shouldDebounce) {if (shouldDebounce) {mWmService.mH.postDelayed(() -> {synchronized (mWmService.mGlobalLock) {if (mFixedRotationLaunchingApp != null&& startAsyncRotation(false /* shouldDebounce */)) {// 应用该事务,使动画控制能够立即生效getPendingTransaction().apply();}}}, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS); //250msreturn false;}if (mAsyncRotationController == null) {mAsyncRotationController = new AsyncRotationController(this);mAsyncRotationController.start();return true;}return false;
}

动画执行

  1. 收集目标窗口
// AsyncRotationController.java
AsyncRotationController(DisplayContent displayContent) {.....// 收集那些可以异步旋转而不阻塞display的窗口。displayContent.forAllWindows(this, true /* traverseTopToBottom */);......
}public void accept(WindowState w) {if (!w.mHasSurface || !canBeAsync(w.mToken)) {return;}if (mTransitionOp == OP_LEGACY && w.mForceSeamlesslyRotate) {// Legacy transition already handles seamlessly windows.return;}......// 大部分都是执行fade窗口动画final int action = mTransitionOp == OP_CHANGE_MAY_SEAMLESS || w.mForceSeamlesslyRotate? Operation.ACTION_SEAMLESS : Operation.ACTION_FADE;mTargetWindowTokens.put(w.mToken, new Operation(action));
}
  1. 目标窗口在TO_FRONT transition启动时淡出
    在这里插入图片描述
07-11 15:27:24.362  3750  3785 D AsyncRotation: Start fade-out Window{ae32994 u0 Floating XXX}
// AsyncRotationController.java
/*** 为可能稍后无缝旋转的窗口令牌准备相应的操作(例如隐藏动画)*/
void start() {.....for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {final WindowToken windowToken = mTargetWindowTokens.keyAt(i);final Operation op = mTargetWindowTokens.valueAt(i);if (op.mAction == Operation.ACTION_FADE || op.mAction == Operation.ACTION_TOGGLE_IME) {fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);op.mLeash = windowToken.getAnimationLeash();if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild());} else if (op.mAction == Operation.ACTION_SEAMLESS) {op.mLeash = windowToken.mSurfaceControl;if (DEBUG) Slog.d(TAG, "Start seamless " + windowToken.getTopChild());}}.....
}
  1. 目标窗口在CHANGE transition启动时以new rotation重绘
    在这里插入图片描述
3750  3785 V WindowManager: Resize reasons for w=Window{eb45fff u0 Floating XXX}:  forceReportingResized=false insetsChanged=true configChanged=true didFrameInsetsChange=true
3750  3785 I WindowManager: Resizing Window{ae32994 u0 Floating XXX} WITH DRAW PENDING
3750  3785 V WindowManager: Requested redraw for orientation change: Window{ae32994 u0 Floating XXX}
3750  7896 I WindowManager: finishDrawing of orientation change: Window{ae32994 u0 Floating XXX} 100ms
// WindowState.java
void updateResizingWindowIfNeeded() {......// display rotation改变, 所以这里的configChanged为truefinal boolean configChanged = !mInRelayout && !isLastConfigReportedToClient();......final boolean contentChanged = didFrameInsetsChange || configChanged|| dragResizingChanged || attachedFrameChanged;.....if (contentChanged || insetsChanged || shouldSendRedrawForSync()) {ProtoLog.v(WM_DEBUG_RESIZE,"Resize reasons for w=%s:  %s configChanged=%b didFrameInsetsChange=%b",this, mWindowFrames.getInsetsChangedInfo(),configChanged, didFrameInsetsChange);.....// 重置当前窗口的mDrawState为DRAW_PENDINGif ((configChanged || getOrientationChanging() || dragResizingChanged)&& isVisibleRequested()) {winAnimator.mDrawState = DRAW_PENDING;.....}if (!mWmService.mResizingWindows.contains(this)) {ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);mWmService.mResizingWindows.add(this);}} .....
}void reportResized() {......ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this,mWindowFrames.mCompatFrame);final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING;if (drawPending) {ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this);}.....final boolean reportDraw = syncRedraw || drawPending;.....if (Flags.bundleClientTransactionFlag()) {getProcess().scheduleClientTransactionItem(WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,alwaysConsumeSystemBars, displayId,syncWithBuffers ? mSyncSeqId : -1, isDragResizing,mLastReportedActivityWindowInfo));onResizePostDispatched(drawPending, prevRotation, displayId);}......
}
  1. TO_FRONT动画结束时开始淡入目标窗口
07-11 15:27:25.532  3750  7896 D AsyncRotation: handleFinishDrawing Window{ae32994 u0 Floating XXX}
07-11 15:27:25.532  3750  7896 D AsyncRotation: Complete set pending Window{ae32994 u0 Floating XXX}
07-11 15:27:25.572  3750  8951 D AsyncRotation: Setup unrotate Window{ae32994 u0 Floating XXX}
07-11 15:27:25.636  3750  7166 D AsyncRotation: Complete directly Window{ae32994 u0 Floating XXX}
07-11 15:27:25.636  3750  7166 D AsyncRotation: finishOp fade-in Window{ae32994 u0 Floating XXX}

在这里插入图片描述

// AsyncRotationController.java
void completeAll() {for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {finishOp(mTargetWindowTokens.keyAt(i));}mTargetWindowTokens.clear();onAllCompleted();
}private void finishOp(WindowToken windowToken) {final Operation op = mTargetWindowTokens.remove(windowToken);......else if (op.mAction == Operation.ACTION_FADE) {if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild());// The previous animation leash will be dropped when preparing fade-in animation, so// simply apply new animation without restoring the transformation.fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);} ......
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/375042.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国产化框架PaddleClas结合Swanlab进行杂草分类

1. 项目介绍 杂草是农业中的主要问题之一,对作物生长和产量造成严重威胁。传统的手动识别和管理方式效率低下且不够精确,因此需要借助先进的计算机视觉技术来提升农业生产的效率和质量。ResNet作为一种深度学习模型,在处理复杂的图像分类任务…

C++入门基础篇(1)

欢迎大家来到海盗猫鸥的博客—— 断更许久,让我们继续好好学习吧! 目录 1.namespace命名空间 命名空间的存在价值: 命名空间的定义: 命名空间的使用: 2.C输入输出函数 使用: 3.缺省参数 4.函数重载…

可观察性优势:掌握当代编程技术

反馈循环是我们开发人员工作的关键。它们为我们提供信息,并让我们从用户过去和现在的行为中学习。这意味着我们可以根据过去的反应进行主动开发。 TestComplete 是一款自动化UI测试工具,这款工具目前在全球范围内被广泛应用于进行桌面、移动和Web应用的…

Redis+Caffeine 实现两级缓存实战

RedisCaffeine 实现两级缓存 背景 ​ 事情的开始是这样的,前段时间接了个需求,给公司的商城官网提供一个查询预计送达时间的接口。接口很简单,根据请求传的城市仓库发货时间查询快递的预计送达时间。因为商城下单就会调用这个接口&#xff…

【RHCE】系统服务综合实验

一、实验内容 现有主机 node01 和 node02,完成如下需求: 1、在 node01 主机上提供 DNS 和 WEB 服务 2、dns 服务提供本实验所有主机名解析 3、web服务提供 www.rhce.com 虚拟主机 4、该虚拟主机的documentroot目录在 /nfs/rhce 目录 5、该目录由 node02…

【Unity2D 2022:UI】制作主菜单

一、创建主菜单游戏场景 1. 在Scenes文件夹中新建一个游戏场景Main Menu 2. 为场景添加背景 (1)创建画布Canvas (2)在Canvas中创建新的空游戏物体Main Menu (3)在Main Menu中新建一个图像游戏物体Backgrou…

达梦数据库dm8安装步骤及迁移

目录 前言: 一、安装部署 1、下载 2、创建用户及安装目录 3、挂载下载的镜像 4、环境配置 5、安装 二、基本使用 1、DM工具使用 2、兼容性配置 2.1 兼容GBK字符集编码 2.2 兼容UTF-8字符集编码 3、创建用户和密码,表空间 4、整理数据库配置 5、启动脚本设置 …

普中51单片机:定时器与计数器详解及应用(七)

文章目录 引言定时器工作原理TMOD定时器/计数器工作模式寄存器定时器工作模式模式0(13位定时器/计数器)模式1(16位定时器/计数器)模式2(8位自动重装模式)模式3(两个8位计数器) 定时器配置流程代码演示——LED1间隔1秒闪烁代码演示——按键1控制LED流水灯状态代码演示——LCD160…

初始网络知识

前言👀~ 上一章我们介绍了使用java代码操作文件,今天我们来聊聊网络的一些基础知识点,以便后续更深入的了解网络 网络 局域网(LAN) 广域网(WAN) 路由器 交换机 网络通信基础 IP地址 端…

法律咨询援助网站

1 项目介绍 1.1 摘要 随着互联网技术的飞速发展,公众对于便捷、高效的法律咨询服务需求日益增长。传统的法律咨询方式已难以满足人们即时性、多样化的咨询需求,促使法律咨询援助网站应运而生。这些平台旨在通过数字化手段,为用户提供法律知…

LayoutLMv2:视觉丰富文档理解的多模态预训练

文本和布局的预训练由于其有效的模型架构和大规模未标记扫描/数字出生文档的优势,在各种视觉丰富的文档理解任务中被证明是有效的。我们提出了具有新的预训练任务的LayoutLMv2架构,以在单个多模态框架中对文本、布局和图像之间的交互进行建模。具体而言&…

Apache防盗链、网页压缩、网页缓存

目录 网页压缩 类型 示例 动态添加模块操作步骤 重装Apache操作步骤 网页缓存 示例 操作步骤 隐藏版本信息 操作步骤 Apache防盗链 定义 原理 配置防盗链实验环境 实验环境 本地图片盗链示例 操作步骤 防盗链示例 操作步骤 网页压缩 网站的访问速度是由多个…

Golang | Leetcode Golang题解之第229题多数元素II

题目: 题解: func majorityElement(nums []int) (ans []int) {cnt : map[int]int{}for _, v : range nums {cnt[v]}for v, c : range cnt {if c > len(nums)/3 {ans append(ans, v)}}return }

oracle 23ai新的后台进程bgnn介绍

前言 昨天发文研究了哪些oracle 后台不能杀 具体文章如下链接 oracle哪些后台进程不能杀?-CSDN博客 其中23ai中新增了一个后台进程bgnn 但是在oracle 23ai database reference中并没有找到该后台进程 有点不甘心就开了个SR,找oracle 官方来看看这个后…

【鸿蒙学习笔记】元服务

官方文档:元服务规格 目录标题 什么是元服务特征第一个元服务-案例介绍创建项目源码启动模拟器启动entry创建卡片出发元服务 什么是元服务 特征 免安装分包预加载老化和更新机制 第一个元服务-案例介绍 创建项目 源码 Entry Component struct WidgetCard {buil…

如何做好IT类的技术面试?

我们在找工作时,需要结合自己的现状,针对意向企业做好充分准备。作为程序员,你有哪些面试IT技术岗的技巧? 方向一:分享你面试IT公司的小技巧 我分享一些基于广泛观察和用户反馈的面试IT公司的小技巧: 技术准…

BUG解决:postman可以请求成功,但Python requests请求报403

目录 问题背景 问题定位 问题解决 问题背景 使用Python的requests库对接物联数据的接口之前一直正常运行,昨天突然请求不通了,通过进一步验证发现凡是使用代码调用接口就不通,而使用postman就能调通,请求参数啥的都没变。 接口…

【JavaScript 算法】快速排序:高效的排序算法

🔥 个人主页:空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 快速排序(Quick Sort)是一种高效的排序算法,通过分治法将数组分为较小的子数组,递归地排序子数组。快速排序通常…

NSIS使用方法

拒绝废话! NSIS下载地址NSIS V3.08 简体中文增强版 - 水晶石 - 博客园 (cnblogs.com) 安装方法参考:使用NSIS打包程序安装包-CSDN博客 打包前需要准备好一个编译好带图标的程序文件,否则安装程序文件产生的快捷方式无图标! 一…

Echarts实现github提交记录图

最近改个人博客&#xff0c;看了github的提交记录&#xff0c;是真觉得好看。可以移植到自己的博客上做文章统计 效果如下 代码如下 <!DOCTYPE html> <html lang"en" style"height: 100%"><head><meta charset"utf-8"> …