能力
JankStats 库可帮助您跟踪和分析应用程序中的性能问题。Jank 是指渲染时间过长的应用程序帧,JankStats 库提供有关应用程序卡顿统计信息的报告。
JankStats 建立在现有 Android 平台功能之上,包括 Android 7(API 级别 24)及更高版本上的FrameMetrics API或早期版本上的OnPreDrawListener 。这些机制可以帮助应用程序跟踪完成帧需要多长时间。
用法
添加依赖
implementation "androidx.metrics:metrics-performance:1.0.0-alpha03"
初始化
class JankLoggingActivity : AppCompatActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
// metrics state holder can be retrieved regardless of JankStats initialization
val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)
// 初始化JankStats jankFrameListener在用于将信息从平台传递到内部 JankStats 的同一线程上调用
jankStats = JankStats.createAndTrack(window, jankFrameListener)
//这个因子乘以当前的刷新率来计算帧持续时间,超过这个时间的帧被认为有jank,默认是2
jankStats.jankHeuristicMultiplier = 3.0f;
// 添加状态
metricsStateHolder.state?.putState("Activity", javaClass.simpleName)
// ...
}JankLoggingActivity.kt
开启和关闭控制
override fun onResume() {
super.onResume()
jankStats.isTrackingEnabled = true
}
override fun onPause() {
super.onPause()
jankStats.isTrackingEnabled = false
}
反馈异常
private val jankFrameListener = JankStats.OnFrameListener { frameData ->
// A real app could do something more interesting, like writing the info to local storage and later on report it.
Log.v("JankStatsSample", frameData.toString())
}JankLoggingActivity.kt
产物
FrameData(frameStartNanos=36648326900279, frameDurationUiNanos=209972875, frameDurationCpuNanos=211467511, isJank=true, states=[Navigation: Args(null), Destination(com.example.jankstats:id/messageRecycler) label=Message List class=com.example.jankstats.MessageListFragment])
侦听器提供有关对象卡顿的每帧信息 FrameData。这包含有关所请求帧的以下信息:
isjank:一个布尔标志,指示帧中是否发生卡顿。
frameDurationUiNanos:帧的持续时间(以纳秒为单位)。
frameStartNanos:帧开始的时间(以纳秒为单位)。
states:您的应用程序在帧中的状态。
原理
初始化 JankStats:传染当前activity的window和监听器
JankStats.createAndTrack(window, jankFrameListener)
init {
val decorView: View? = window.peekDecorView()
if (decorView == null) {
throw IllegalStateException(
"window.peekDecorView() is null: " +
"JankStats can only be created with a Window that has a non-null DecorView"
)
}
//创建holder,用来保存状态
holder = PerformanceMetricsState.create(decorView)
//根据版本初始化对应的
implementation =JankStats,获取帧率的方式不同
when {
Build.VERSION.SDK_INT >= 31 -> {
JankStatsApi31Impl(this, decorView, window)
}
Build.VERSION.SDK_INT >= 26 -> {
JankStatsApi26Impl(this, decorView, window)
}
Build.VERSION.SDK_INT >= 24 -> {
JankStatsApi24Impl(this, decorView, window)
}
Build.VERSION.SDK_INT >= 22 -> {
JankStatsApi22Impl(this, decorView)
}
Build.VERSION.SDK_INT >= 16 -> {
JankStatsApi16Impl(this, decorView)
}
else -> {
JankStatsBaseImpl(this)
}
}
//设置监听回调
implementation.setupFrameTimer(true)
}
>= api16 setupFrameTimer
//创建ViewTreeObserver.OnPreDrawListener添加到viewTreeObserver
//在OnPreDrawListener中获取当前帧开始时间,当前时间,帧预期耗时
//调用OnFrameListenerDelegate的onFrame(开始时间,now - 开始时间,预期时间)方法
//在onFrame中判断是不是jank
override fun setupFrameTimer(enable: Boolean) {
val decorView = decorViewRef.get()
decorView?.let {
if (enable) {
//通过ViewTreeObserver.OnPreDrawListene来计算当前帧的耗时
val delegates = decorView.getOrCreateOnPreDrawListenerDelegator()
delegates.add(onFrameListenerDelegate)
} else {
decorView.removeOnPreDrawListenerDelegate(onFrameListenerDelegate)
}
}
}
端上注册了jankStats.logFrameData监听
private val onFrameListenerDelegate = object : OnFrameListenerDelegate() {
override fun onFrame(startTime: Long, uiDuration: Long, expectedDuration: Long) {
jankStats.logFrameData(getFrameData(startTime, uiDuration,
(expectedDuration * jankStats.jankHeuristicMultiplier).toLong()))
}
}
internal open fun getFrameData(
startTime: Long,
uiDuration: Long,
expectedDuration: Long
): FrameData {
metricsStateHolder.state?.getIntervalStates(startTime, startTime + uiDuration,
stateInfo)
//判断是不是jank
val isJank = uiDuration > expectedDuration
frameData.update(startTime, uiDuration, isJank)
return frameData
}