App布局层级分析和渲染
前沿
关于布局分析的文章,网上太多Hierarchy Viewer相关的文章,但是现在Android Studio 都更新到3.6.X了,这个工具早废弃了,能够真正有用的文章少之又少,所有才有了本篇的文章,使用最新的工具进行App布局层级分析和渲染
目的
基于最新的Android Studio 版本(目前3.6.1),使用最新的工具,进行Android 布局的层级分析和渲染速度分析
一、层级分析
使用Layout Inspector即可实现布局层级的分析,这个还是比较简单的,直接打开tools
1、选择tools->Layout Inspector
2、在随即显示的 Choose Process 对话框中,选择想要检查的应用进程,然后点击 OK,布局检查器会拍摄快照,将其另存为 .li 文件并打开。
3、获取的文件如下图所示
二、渲染速度分析
网上大部分文章都是使用Hierarchy Viewer进行渲染的分析,实际这个以及过时很久了,3.6.2工具都移除了,所以这里使用最新的工具进行渲染速度的分析
官方推荐方法:使用 Window.OnFrameMetricsAvailableListener进行渲染速度的分析
从Android 7.0 (API level 24)开始,Android引入Window.OnFrameMetricsAvailableList接口用于提供每一帧绘制各阶段的耗时,数据源与GPU Profile相同.
查看源码如下:
public interface OnFrameMetricsAvailableListener {
void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,int dropCountSinceLastInvocation);
}
/**
* 包含1帧的周期内,渲染系统各个方法的耗时数据.
*/
public final class FrameMetrics {
****
//通过getMetric获取layout/measure耗时所用的id
public static final int LAYOUT_MEASURE_DURATION = 3;
public static final int DRAW_DURATION = 4;
/**
* 获取当前帧指定id代表的方法/过程的耗时,单位是纳秒:1纳秒(ns)=10的负6次方毫秒(ms)
*/
public long getMetric(@Metric int id) {
****
}
}
各项指标:
FrameMetrics 指标 | 说明 |
ANIMATION_DURATION | 表示执行动画回调的耗时 |
COMMAND_ISSUE_DURATION | 表示向 GPU 发出绘制命令的耗时 |
DRAW_DURATION | 表示将 View 树转换为 DisplayList 的耗时 |
FIRST_DRAW_FRAME | 表示当前帧是否是当前 Window 布局中绘制的第一帧 |
INPUT_HANDLING_DURATION | 表示处理输入事件回调的耗时 |
INTENDED_VSYNC_TIMESTAMP | 当前帧的预期开始时间, 如果此值与 VSYNC_TIMESTAMP 不同,则表示 UI 线程上发生了阻塞,阻止了 UI 线程及时响应vsync信号 |
LAYOUT_MEASURE_DURATION | 表示对 View 树进行 measure 和 layout 所花的时间 |
SWAP_BUFFERS_DURATION | 表示将此帧的帧缓冲区发送给显示子系统所花的时间 |
SYNC_DURATION | 表示将 DisplayList 与渲染线程同步所花的时间 |
TOTAL_DURATION | 表示此帧渲染并发布给显示子系统所花费的总时间, 等于所有其他具有时间价值的指标的值之和 |
UNKNOWN_DELAY_DURATION | 表示等待 UI 线程响应并处理帧所经过的时间, 大多数情况下应为0 |
VSYNC_TIMESTAMP | 在所有 vsync 监听器和帧绘制中使用的时间值(Choreographer 的帧回调, 动画, View#getDrawingTime等) |
时间单位都是纳秒 |
以上所有指标都存储在一个 long 类型的数组中, 使用 FrameMetrics#getMetric 方法可以从中提取。
在Activity中的使用
新建一个函数,设置addOnFrameTetricsAvailableIListener
val thread = HandlerThread("frame-stat").apply { start() }
val handler = Handler(thread.looper)
window.addOnFrameMetricsAvailableListener(Window.OnFrameMetricsAvailableListener { _, metric,_ ->
// 会在 handler 对应的 thread 中执行
val copy = FrameMetrics(metric) /* 注意需要做深拷贝, 再使用 */
val vsycn = copy.getMetric(FrameMetrics.VSYNC_TIMESTAMP)
val intended = copy.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP)
Log.d("FrameStat", "is first frame: ${copy.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1L} ")
Log.d("FrameStat", "measure layout: ${copy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) / 1000000} ms")
Log.d("FrameStat", "draw: ${copy.getMetric(FrameMetrics.DRAW_DURATION) / 1000000} ms")
Log.d("FrameStat", "total: ${copy.getMetric(FrameMetrics.TOTAL_DURATION) / 1000000} ms")
Log.d("FrameStat", "delay draw: ${copy.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP) > copy.getMetric(FrameMetrics.VSYNC_TIMESTAMP)} ms")
Log.d("FrameStat", "=============")
}, handler)
运行结果如下: