【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


相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
目录
相关文章
|
XML 存储 Android开发
【Android 性能优化】布局渲染优化 ( CPU 与 GPU 架构分析 | 安卓布局显示流程 | 视觉与帧率分析 | 渲染超时卡顿分析 | 渲染过程与优化 )
【Android 性能优化】布局渲染优化 ( CPU 与 GPU 架构分析 | 安卓布局显示流程 | 视觉与帧率分析 | 渲染超时卡顿分析 | 渲染过程与优化 )
380 0
【Android 性能优化】布局渲染优化 ( CPU 与 GPU 架构分析 | 安卓布局显示流程 | 视觉与帧率分析 | 渲染超时卡顿分析 | 渲染过程与优化 )
|
8月前
|
XML 缓存 Java
Android 启动优化(六)- 深入理解布局优化
Android 启动优化(六)- 深入理解布局优化
|
XML 缓存 IDE
Android修行手册-卡顿优化布局篇【建议收藏】
众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!
203 0
|
Android开发
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(二)
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(二)
276 0
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(二)
|
Android开发
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(一)
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(一)
485 0
【Android 安装包优化】Tint 着色器 ( 简介 | 布局文件中的 Tint 着色器基本用法 | 代码中使用 Tint 着色器添加颜色效果 )(一)
|
Android开发 Java
android 优化之布局优化
布局优化的思路很简单,尽量减少布局文件的层级,看过系统源码的都知道,Android view绘制都是逐层绘制的,所以布局的层级少了,decodeview的时候绘制工作自然就少了。 那么如何进行布局的优化呢?首先删除无用的布局后,我们会优先选择性能较高的ViewGroup,比如在一个界面中,既可以用LinearLayout,也可以用RelativeLayout,那么我们优先选择Linear
1166 0
|
10天前
|
存储 安全 Android开发
安卓应用开发:构建一个高效的用户登录系统
【5月更文挑战第3天】在移动应用开发中,用户登录系统的设计与实现是至关重要的一环。对于安卓平台而言,一个高效、安全且用户体验友好的登录系统能够显著提升应用的用户留存率和市场竞争力。本文将探讨在安卓平台上实现用户登录系统的最佳实践,包括对最新身份验证技术的应用、安全性考量以及性能优化策略。