【Android 性能优化】布局渲染优化 ( CPU 渲染优化 | 减少布局的嵌套 | 测量布局绘制时间 | OnFrameMetricsAvailableListener | 布局渲染优化总结 )

简介: 【Android 性能优化】布局渲染优化 ( CPU 渲染优化 | 减少布局的嵌套 | 测量布局绘制时间 | OnFrameMetricsAvailableListener | 布局渲染优化总结 )

文章目录

一、 减少布局嵌套

二、 布局渲染时间测量

1. FrameMetrics 使用流程

2. FrameMetrics 参数解析

3. FrameMetrics 代码示例

三、 布局渲染优化总结





一、 减少布局嵌套


在 【Android 性能优化】布局渲染优化 ( GPU 过度绘制优化总结 | CPU 渲染过程 | Layout Inspector 工具 | View Tree 分析 | 布局组件层级分析 ) 博客中引入了 CPU 渲染优化 , CPU 渲染优化的核心就是减少布局嵌套 , 布局嵌套使用 Android Studio 中的 Layout Inspector 工具进行查看 ;



CPU 渲染的优化的核心就是减少布局的嵌套 , 推荐使用约束布局进行开发 , 只有一层嵌套的布局 ;


减少布局的嵌套 , 能极大减少 UI 组件测量 , 摆放 , 生成 UI 组件的时间 , 这样就可以减少 CPU 渲染时间 , 使整个渲染过程时间降低 , 尽可能的压缩在 16ms 以内 , 保证 Vsync 信号到来时 , 渲染已经完毕 , 可以在屏幕中绘制这些布局 ;



能够被优化的布局 : 假如父布局中只有一个子布局 , 子布局中有若干组件 , 那么可以直接将子布局的组件放在父布局中 , 将子布局这个层级干掉 , 或者将父布局层级删除 ;


一个父布局没有分支的布局 , 只有一个子布局 , 那么大概率可以优化删除父布局或子布局中的一个 , 两者保留一个 ;



强烈推荐使用 ConstraintLayout 约束布局 , 没有以上布局嵌套问题 ;






二、 布局渲染时间测量


如果使用 可以直接在该工具中查看布局渲染时间 , 但是该工具停止维护 , 使用老版本的 Android Studio 可以使用该工具 ;


Google 官方推荐使用 OnFrameMetricsAvailableListener 测量布局渲染时间 ;




1. FrameMetrics 使用流程


FrameMetrics 使用流程 :



① 创建测量线程 : 测量的过程肯定是要放在线程中执行 , 这里创建 HandlerThread 线程 ; 该线程创建后直接启动即可 ;


HandlerThread handlerThread = new HandlerThread("FrameMetrics");
handlerThread.start();


② 创建 Handler : 测量时 , 需要将 Handler 对象传递到 addOnFrameMetricsAvailableListener 方法中 , 与 Window.OnFrameMetricsAvailableListener 监听器一起设置 ;


Handler handler = new Handler(handlerThread.getLooper());



③ 注册监听器 : 向 Activity 界面的 Window 窗口对象 , 添加监听器 , 同时设置 Handler , 渲染测量过程在该 Handler 所在线程执行 ;


getWindow().addOnFrameMetricsAvailableListener(
                    new Window.OnFrameMetricsAvailableListener(){
  // 省略实现内容 ... 
}, handler);


④ 回调方法中获取渲染时间 : FrameMetrics frameMetrics 参数中封装了 12 1212 个渲染性能参数属性 , 可以自行




2. FrameMetrics 参数解析


FrameMetrics 参数解析 :



FrameMetrics 参数常量 代表的含义

ANIMATION_DURATION 动画执行回调时间, 单位纳秒

COMMAND_ISSUE_DURATION 向 GPU 发送绘制命令花费的时间, 单位纳秒

DRAW_DURATION 将组件树 ( View Hierarchy ) 转为显示列表 ( DisplayLists ) , 计算过程所花费的时间, 单位纳秒

FIRST_DRAW_FRAME 绘制的该帧是否是第一帧, 0 是, 1 不是 ; 第一帧渲染会慢一些 , 第一帧不会引发动画中的跳帧问题, 这些问题都会被窗口动画隐藏 , 不必进行显示过程中的 jank 计算

INPUT_HANDLING_DURATION 处理输入事件花费的时间, 单位纳秒

INTENDED_VSYNC_TIMESTAMP 该值是个时间戳, 表示该帧的 vsync 信号发出时间 ; 这个时间是当前帧的预期开始时间 , 如果该时间与 VSYNC_TIMESTAMP 时间戳不同 , 那么说明 UI 线程被阻塞了, 没有及时响应 vsync 信号

LAYOUT_MEASURE_DURATION 组件树 ( view hierarchy ) 测量 ( measure ) 和摆放 ( layout ) 花费的时间 , 单位纳秒

SWAP_BUFFERS_DURATION CPU 传递多维向量图形数据给 GPU 花费的时间, 单位纳秒

SYNC_DURATION 显示列表 ( DisplayLists ) 与显示线程同步花费的时间, 单位纳秒

TOTAL_DURATION CPU 渲染到传递到 GPU 所用的总时间, 上述所花费的有意义的时间之和 , 单位纳秒

UNKNOWN_DELAY_DURATION UI 线程响应并开始处理渲染的等待时间, 一般是 0, 如果大于 0 说明出问题了

VSYNC_TIMESTAMP vsync 信号发出的时间戳, 该时刻 GPU 应该进行绘制, 间隔 16ms , 同时 CPU 开始渲染



3. FrameMetrics 代码示例


代码示例 :


public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 渲染性能测量
        renderingPerformanceMeasurement();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    /**
     * 渲染性能测量
     */
    public void renderingPerformanceMeasurement(){
        HandlerThread handlerThread = new HandlerThread("FrameMetrics");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());
        // 24 版本以后的 API 才能支持该选项
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 整个渲染过程测量都在 HandlerThread 线程中执行
            getWindow().addOnFrameMetricsAvailableListener(
                    new Window.OnFrameMetricsAvailableListener(){
                        @Override
                        public void onFrameMetricsAvailable(
                                Window window,
                                FrameMetrics frameMetrics,
                                int dropCountSinceLastInvocation) {
                            // 帧渲染测量完毕后, 会回调该方法
                            // 渲染性能参数都封装在 FrameMetrics frameMetrics 参数中
                            // 先拷贝一份, 之后从拷贝的数据中获取对应渲染时间参数
                            FrameMetrics fm = new FrameMetrics(frameMetrics);
                            // 1. 动画执行回调时间, 单位纳秒
                            Log.i("FrameMetrics", "ANIMATION_DURATION : " +
                                    fm.getMetric(FrameMetrics.ANIMATION_DURATION));
                            // 2. 向 GPU 发送绘制命令花费的时间, 单位纳秒
                            Log.i("FrameMetrics", "COMMAND_ISSUE_DURATION : " +
                                    fm.getMetric(FrameMetrics.COMMAND_ISSUE_DURATION));
                            // 3. 将组件树 ( View Hierarchy ) 转为显示列表 ( DisplayLists )
                            // 计算过程所花费的时间, 单位纳秒
                            Log.i("FrameMetrics", "DRAW_DURATION : " +
                                    fm.getMetric(FrameMetrics.DRAW_DURATION));
                            // 4. 绘制的该帧是否是第一帧, 0 是, 1 不是
                            // 第一帧渲染会慢一些
                            // 第一帧不会引发动画中的跳帧问题, 这些问题都会被窗口动画隐藏
                            // 不必进行显示过程中的 jank 计算
                            Log.i("FrameMetrics", "FIRST_DRAW_FRAME : " +
                                    fm.getMetric(FrameMetrics.FIRST_DRAW_FRAME));
                            // 5. 处理输入事件花费的时间, 单位纳秒
                            Log.i("FrameMetrics", "INPUT_HANDLING_DURATION : " +
                                    fm.getMetric(FrameMetrics.INPUT_HANDLING_DURATION));
                            // 6. 该值是个时间戳, 表示该帧的 vsync 信号发出时间
                            // 这个时间是当前帧的预期开始时间
                            // 如果该时间与 VSYNC_TIMESTAMP 时间戳不同
                            // 那么说明 UI 线程被阻塞了, 没有及时响应 vsync 信号
                            Log.i("FrameMetrics", "INTENDED_VSYNC_TIMESTAMP : " +
                                    fm.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP));
                            // 7. 组件树 ( view hierarchy ) 测量 ( measure ) 和摆放 ( layout ) 花费的时间
                            // 单位 纳秒
                            Log.i("FrameMetrics", "LAYOUT_MEASURE_DURATION : " +
                                    fm.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION));
                            // 8. CPU 传递多维向量图形数据给 GPU 花费的时间, 单位纳秒
                            Log.i("FrameMetrics", "SWAP_BUFFERS_DURATION : " +
                                    fm.getMetric(FrameMetrics.SWAP_BUFFERS_DURATION));
                            // 9. 显示列表 ( DisplayLists ) 与显示线程同步花费的时间, 单位纳秒
                            Log.i("FrameMetrics", "SYNC_DURATION : " +
                                    fm.getMetric(FrameMetrics.SYNC_DURATION));
                            // 10. CPU 渲染到传递到 GPU 所用的总时间, 上述所花费的有意义的时间之和
                            // 单位纳秒
                            Log.i("FrameMetrics", "TOTAL_DURATION : " +
                                    fm.getMetric(FrameMetrics.TOTAL_DURATION));
                            // 11. UI 线程响应并开始处理渲染的等待时间, 一般是 0, 如果大于 0 说明出问题了
                            Log.i("FrameMetrics", "UNKNOWN_DELAY_DURATION : " +
                                    fm.getMetric(FrameMetrics.UNKNOWN_DELAY_DURATION));
                            // 12. vsync 信号发出的时间戳, 该时刻 GPU 应该进行绘制, 间隔 16ms
                            // 同时 CPU 开始渲染
                            Log.i("FrameMetrics", "VSYNC_TIMESTAMP : " +
                                    fm.getMetric(FrameMetrics.VSYNC_TIMESTAMP));
                        }
                    }, handler);
        }
    }
}





打印结果 :


2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: ANIMATION_DURATION : 3854
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: COMMAND_ISSUE_DURATION : 101644281
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: DRAW_DURATION : 9981616
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: FIRST_DRAW_FRAME : 1
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: INPUT_HANDLING_DURATION : 50625
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: INTENDED_VSYNC_TIMESTAMP : 164845526994092
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: LAYOUT_MEASURE_DURATION : 154012359
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: SWAP_BUFFERS_DURATION : 1569062
2020-06-25 13:19:05.339 14179-14229/kim.hsl.rtmp I/FrameMetrics: SYNC_DURATION : 405729
2020-06-25 13:19:05.340 14179-14229/kim.hsl.rtmp I/FrameMetrics: TOTAL_DURATION : 268872818
2020-06-25 13:19:05.340 14179-14229/kim.hsl.rtmp I/FrameMetrics: UNKNOWN_DELAY_DURATION : 639354
2020-06-25 13:19:05.340 14179-14229/kim.hsl.rtmp I/FrameMetrics: VSYNC_TIMESTAMP : 164845526994092
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: ANIMATION_DURATION : 281042
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: COMMAND_ISSUE_DURATION : 1590885
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: DRAW_DURATION : 520157
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: FIRST_DRAW_FRAME : 1
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: INPUT_HANDLING_DURATION : 38646
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: INTENDED_VSYNC_TIMESTAMP : 164845543729270
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: LAYOUT_MEASURE_DURATION : 6619844
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: SWAP_BUFFERS_DURATION : 908230
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: SYNC_DURATION : 51302
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: TOTAL_DURATION : 284281446
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: UNKNOWN_DELAY_DURATION : 274133736
2020-06-25 13:19:05.371 14179-14229/kim.hsl.rtmp I/FrameMetrics: VSYNC_TIMESTAMP : 164845810395926
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: ANIMATION_DURATION : 206980
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: COMMAND_ISSUE_DURATION : 2708386
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: DRAW_DURATION : 86823
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: FIRST_DRAW_FRAME : 0
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: INPUT_HANDLING_DURATION : 51302
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: INTENDED_VSYNC_TIMESTAMP : 164845828222860
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: LAYOUT_MEASURE_DURATION : 685260
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: SWAP_BUFFERS_DURATION : 859895
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: SYNC_DURATION : 537292
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: TOTAL_DURATION : 5902960
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: UNKNOWN_DELAY_DURATION : 545772
2020-06-25 13:19:05.377 14179-14229/kim.hsl.rtmp I/FrameMetrics: VSYNC_TIMESTAMP : 164845828222860


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
236 4
|
3月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
80 2
|
3月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
|
1月前
|
网络协议 Linux Android开发
深入探索Android系统架构与性能优化
本文旨在为读者提供一个全面的视角,以理解Android系统的架构及其关键组件。我们将探讨Android的发展历程、核心特性以及如何通过有效的策略来提升应用的性能和用户体验。本文不包含常规的技术细节,而是聚焦于系统架构层面的深入分析,以及针对开发者的实际优化建议。
65 1
|
2月前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
2月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
46 5
|
2月前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
74 3
|
3月前
|
存储 缓存 网络协议
5个Android性能优化相关的深度面试题
本文涵盖五个Android面试题及其解答,包括优化应用启动速度、内存泄漏的检测与解决、UI渲染性能优化、减少内存抖动和内存溢出、优化网络请求性能。每个问题都提供了详细的解答和示例代码。
51 2
|
3月前
|
监控 测试技术 Android开发
掌握安卓性能优化的关键策略
【10月更文挑战第7天】 在移动应用开发领域,性能优化是一项至关重要的任务。本文将探讨安卓应用性能优化的重要性、关键策略以及实际操作建议,帮助开发者提升应用的用户体验和竞争力。通过深入浅出的方式,我们将从背景介绍到具体实践,全面解析安卓性能优化的各个维度。
|
4月前
|
存储 Java 编译器
🔍深入Android底层,揭秘JVM与ART的奥秘,性能优化新视角!🔬
【9月更文挑战第12天】在Android开发领域,深入了解其底层机制对提升应用性能至关重要。本文详述了从早期Dalvik虚拟机到现今Android Runtime(ART)的演变过程,揭示了ART通过预编译技术实现更快启动速度和更高执行效率的奥秘。文中还介绍了ART的编译器与运行时环境,并提出了减少DEX文件数量、优化代码结构及合理管理内存等多种性能优化策略。通过掌握这些知识,开发者可以从全新的角度提升应用性能。
87 11