了解AsyncRotationController

news2024/11/12 17:48:16

概述

基于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); //250ms
        return 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为true
    final 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_PENDING
        if ((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.coloradmin.cn/o/1919366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【记录】CSS|Tailwind 的主题定义的颜色的使用方法(--color啥的)

文章目录 【记录】CSS|Tailwind 的主题定义的颜色的使用方法(--color 啥的)省流版GPT 详细解释版Tailwind CSS 配置文件示例使用自定义颜色定义 CSS 变量总结 附赠个 Tips 【记录】CSS|Tailwind 的主题定义的颜色的使用方法&#…

【Python】已解决:ModuleNotFoundError: No module named ‘sklearn.cross_validation

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 (机器学习分割数据问题)解决“ModuleNotFoundError: No module named ‘sklearn.cross_validation’” 一、问题背景 在机器学习的实践中,数据分割是…

聚鼎装饰画:现在做装饰画能不能相信

在艺术的殿堂中,装饰画以其多变的风格和独特的魅力占据了一席之地。它们或清新淡雅,或浓烈奔放,总能为现代家居带来一丝生气与美感。然而,在这美丽的背后,却隐藏着一个令人困惑的问题:现在做装饰画&#xf…

[EasilyOpenJCL] 使用 Java 调用显卡 计算 和Java调用 CPU 进行计算 的基准测试!

设备环境介绍 easily-openJCL 是一个轻量级的 Java 语言下的 GPU 显卡 计算库,它提供了一套简单易用的 API,让用户能够轻松实现 GPU 计算操作。 通过 Java 调用 GPU 计算的一个库,使用非常简单的API就可以轻松应付 Java 数据类型在 GPU 中的…

程序使用多进程,打包.exe后,程序陷入死循环

最近写了一个深度学习程序,用cxfreezee打包exe后,在本地运行突然出现死循环,明明在pycharm运行一切正常。 排查了问题,怀疑是多进程的原因,解决办法: 在你的主程序前添加一行代码: if __name_…

Typescript 模块小知识-global scope

问题表现 在编写ts代码的时候遇到一个问题, 表现为, 如果在某个ts工程中, 如果多个文件里面没有任何导出export或者是export default, 那么这些文件如果有const或者是let定义相同的声明都会报错如下 无法重新声明块范围变量 a/a.ts 和 index.ts 和 index2.ts 都没有进行expor…

C++系列-Vector(一)

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” Vector的介绍及使用 Vector的介绍 当vector构建的参数类型为char类型时,它是和string是极其类似的,但是二者之间也有不同,比如&#xff0c…

人工智能时代,零基础学IT,我首推Python作为你编程入门语言!

人工智能时代为什么将 Python 称为第一语言? 因为python适应了人工智能时代: 人工智能时代对于代码的简便性有很大要求,像传统的C/CPP/Java学习较为复杂,学习路线长,对于很多零基础的人入门困难。python的兼容性&…

24下软考高级-系统架构设计师100条知识点速记!

11月系统架构设计师现在准备真的太早了吗? 不不不~对于0基础和打工人,留给我们备考的时间其实已经不多了! 想开始不晓得从哪里开始?这里给大家整理了24下系统架构设计师知识点100条,符合最新版教材和考试大纲&#x…

Python实现动态迷宫生成:自动生成迷宫的动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义迷宫生成类主循环 完整代码 引言 迷宫生成算法在游戏开发和图形学中有着广泛的应用。它不仅可以用于创建迷宫游戏,还可以用于生成有趣的图案。在这篇博客中,我们将使用Python…

目前哪些充电宝是最强?曝光几款好用的充电宝排行榜

充电宝在我们的日常生活中能为我们带来无与伦比的便捷与体验。然而,当充电宝市场品牌和型号众多,功能丰富多样,口碑最佳的充电宝究竟有哪些?怎样才能挑选到口碑上佳、契合个人需求的充电宝,这已然成为消费者面临的难题…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【HMAC(C/C++)】

HMAC(C/C) HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),是一种基于Hash函数和密钥进行消息认证的方法。 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 生…

基于Python/MATLAB长时间序列遥感数据处理及在全球变化、植被物候提取、植被变绿与生态系统固碳分析、生物量估算与趋势分析应用

植被是陆地生态系统中最重要的组分之一,也是对气候变化最敏感的组分,其在全球变化过程中起着重要作用,能够指示自然环境中的大气、水、土壤等成分的变化,其年际和季节性变化可以作为地球气候变化的重要指标。此外,由于…

大模型【Qwen2-7B本地部署(WEB版)】(windows)

大模型系列文章目录 Qwen2-7B本地部署(WEB版) 前言 大模型是截止2024年上半年最强的AI,Qwen2是刚出来的号称国内最强开源大模型。这是大模型系列的第一篇文章,旨在快速部署看看最近出来的大模型效果怎么样,效果ok的话…

Nature Communications|柔性无感智能隐形眼镜(柔性传感/可穿戴电子/柔性电子)

南京大学徐飞(Fei Xu)、陆延青(Yanqing Lu)、陈烨(Ye Chen)和江苏省人民医院袁松涛(Songtao Yuan)团队,在《Nature Communications》上发布了一篇题为“Frequency-encoded eye tracking smart contact lens for human–machine interaction”的论文。论文内容如下: 一、 摘…

【Pytorch】Conda环境pack打包迁移报错处理

文章目录 Anaconda虚拟环境打包一、源电脑的环境打包1.安装conda-pack工具2.确定环境3.打包环境4.将打包环境拷贝到U盘 二、环境迁移到目标电脑上三、异常处理pip install -e. 导致无法pack→忽略管理的文件已经被删除或者被覆盖→压缩成tar注意 重新激活环境 Anaconda虚拟环境…

Dify中的知识库API列表

1.知识库API列表 通过文本/文件创建/更新/删除文档/查询文档嵌入状态,知识库创建/知识库查询/文档列表查询,分段增/删/改/查。 接口名字功能描述请求示例POST/datasets/{dataset_id}/document/create_by_text通过文本创建文档此接口基于已存在知识库&a…

法律咨询援助网站

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

Python基础教学之二:核心篇——数据结构与流程控制

Python基础教学之二:核心篇——数据结构与流程控制 一、深入理解数据类型 1. 字符串、数字和布尔类型 字符串操作:Python中的字符串可以通过多种内置方法和格式化工具进行操作。例如使用str.format()方法来格式化字符串,或者通过str.join()…

冒泡排序与其C语言通用连续类型排序代码

冒泡排序与其C语言通用连续类型排序代码 冒泡排序冒泡排序为交换排序的一种:动图展示:冒泡排序的特性总结:冒泡排序排整型数据参考代码(VS2022C语言环境): 冒泡排序C语言通用连续类型排序代码对比较的方式更…