共计 4558 个字符,预计需要花费 12 分钟才能阅读完成。
△ 插图作者: Virginia Poltrack
作者 / Chet Haase, Android 开发技术推广工程师
卡顿 (名词): 指利用性能蹩脚,可能导致丢帧、界面动画不连贯和用户体验不佳等问题。请参阅 “ 不开心的用户 ” 词条。
性能问题很难调试。咱们经常不分明要从何下手、应用何种工具、用户遇到了什么问题,以及那些问题在事实的设施上有何体现。
过来几年间,Android 团队始终致力推出更多工具,用于调试各种问题,从剖析 启动性能 到测试 具体代码门路,再到测试和优化特定 用例 及 IDE 中的可视化分析器,各畛域均有涉猎。所有这些工具均针对开发期间的测试设计,用于帮忙您调试和修复在本地运行时发现的问题。
同时,Google Play 的 Android Vitals 和 Firebase 均提供信息中心,供开发者理解其利用在理论用户设施上的运行状况。
尽管如此,在理论状况中,咱们依然很难发现利用中可能存在的问题,尤其是用户设施上可能呈现的问题。这可不是您坐在座椅中用着相熟的开发机器能碰到的问题。虽说性能信息中心可提供肯定帮忙,但在用户遇到问题时,它却未必能让您充沛理解所产生状况的详细信息。
JankStats 应运而生: 这是首个专为在用户设施上检测及报告利用的性能问题而构建的 AndroidX 库。
JankStats 是占用空间绝对较小的 API,次要有三大指标: 捕捉每帧的性能信息、在用户设施 (不仅是开发设施) 上运行、以及在利用呈现性能问题时启用检测,并报告所产生的状况。
每帧性能
Android 平台已提供多种办法,用于获取帧性能数据。例如,从 API 24 开始就能够应用 FrameMetrics 获取相干数据,后续多个版本也在进一步丰盛该性能,以便为您提供更多详细信息。如果在更晚期的版本上运行利用,也有多种办法可供您获取工夫信息,虽说不够精确,但仍非常实用。
因而,如果您想确保本人的帧持续时间逻辑实用于所有版本,就须要在不同的 API 版本中实现不同的测试和报告机制。当初,您能够应用对立的 JankStats API 来实现这些性能。除此之外,它还提供了更多惊喜 (请持续浏览本文!)。
JankStats 通过提供繁多 API 来报告每帧的工夫,从而简化您的工作,并会在外部委派适当机制 (比方 API 24 以上会委派给 FrameMetrics)。您不用关怀这些数据的起源,只需让 JankStats 告诉您实现特定事项破费的工夫,而后便可在回调中获取相干信息。
创立和监听 JankStats 数据就是这么简略: 只需实现创立,而后就能够坐下来 (好吧,是您的代码 “ 坐 ” 下来) 监听。以下是 JankStats 的示例 JankLoggingActivity 中的步骤范例:
val jankFrameListener = JankStats.OnFrameListener { frameData ->
// real app would do something more interesting than log this...
Log.v("JankStatsSample", frameData.toString())
}
jankStats = JankStats.createAndTrack(
window,
Dispatchers.Default.asExecutor(),
jankFrameListener,
)
此处的 Log.v() 调用仅作范例应用,并非您在利用中应采取的操作。在实际操作中,您可能应汇整 / 贮存 / 上传数据,以供日后剖析应用,而非将数据公布于日志中。无论如何,上面是在 API 30 模拟器上运行时产生的输入示例 ( 为便于浏览,已删除局部 logcat 的输入内容,并增加了空白行):
JankStats.OnFrameListener: FrameData(frameStartNanos=827233150542009, frameDurationUiNanos=27779985, frameDurationCpuNanos=31296985, isJank=false, states=[Activity: JankLoggingActivity])
JankStats.OnFrameListener: FrameData(frameStartNanos=827314067288736, frameDurationUiNanos=89903592, frameDurationCpuNanos=94582592, isJank=true, states=[RecyclerView: Dragging, Activity: JankLoggingActivity])
JankStats.OnFrameListener: FrameData(frameStartNanos=827314167288732, frameDurationUiNanos=88641926, frameDurationCpuNanos=91526926, isJank=true, states=[RecyclerView: Settling, RecyclerView: Dragging, Activity: JankLoggingActivity])
JankStats.OnFrameListener: FrameData(frameStartNanos=827314183945923, frameDurationUiNanos=4731405, frameDurationCpuNanos=8283405, isJank=false, states=[RecyclerView: Settling, Activity: JankLoggingActivity])
您能够在日志的 frameData
中看到一些乏味的内容:
- 其中有局部帧带有
isJank=true
标记。该日志取自运行的示例利用JankLoggingActivity
,您可查看 残缺示例 理解更多。该利用会强制产生一些长帧 (没错,用了Thread.sleep()
!),从而让 JankStats 断定其为卡顿。 - 帧的工夫信息中同时蕴含界面和 CPU 数据,但在 API 24 (
FrameMetrics
被引入的版本 ) 之前的版本中,此信息仅蕴含界面持续时间。 - 该日志是从我在利用中开始滑动 RecyclerView 时获取的。当 RecyclerView 开始挪动 (被 “ 拖动 ”) 以及 RecyclerView 开始自在滚动 (被 “ 搁置 ”) 时,咱们可在开始之前看到与界面状态相干的信息 (仅列出
Activity
状态 )。无关这些界面状态的详细信息,请浏览下文。
实在数据
不同于最近的基准库,创立 JankStats 的目标是为您提供来自用户设施的后果。能在开发机器上调试问题诚然很好,但在事实中,用户会依据迥异的约束条件,在不同的设施上应用您的利用,对于这类状况,本地调试可能并不能发现和解决问题。
JankStats 提供 API 来检测您的利用,以提供您所需的性能数据和报告机制,以便您能上传这些数据并离线进行剖析。
利用状态
最初 (请留神,这才是 JankStats 库的新亮点),JankStats 提供了一种办法,可让您理解呈现性能问题时利用中理论产生的状况。咱们常常听到的埋怨是: 现有的工具、信息中心和办法均未能提供足够的背景信息,不足以让您通晓用户理论遭逢到的性能问题。
例如,FrameMetrics API (在 API 24 版本中推出,JankStats 外部也有应用) 能够告诉您绘制帧须要多长时间,而您也可从中获取卡顿信息,但它无奈让您通晓过后利用中的具体情况。当您尝试检测代码,并将其与 FrameMetrics 或其余性能测量工具集成时,该问题就须要开发者自行解决。然而,除非必须要在外部构建这种基础架构,那每个人都有许多别的工作要做。因而,卡顿问题通常得不到量化测试,而性能问题天然也无奈解决。
同样,Android Vitals 信息中心也能够告诉您,利用存在性能问题,但无奈告诉您问题产生时利用的具体运行状况。因而,您很难通过这些信息来通晓应该如何解决呈现的问题。
JankStats 推出了 PerformanceMetricsState
API,这套简略的办法可让您通过成对的字符串通知零碎在任意时刻您的利用所产生的事件。例如,您可能想晓得,某个特定的 Activity
或 Fragment
在何时处于活动状态,或 RecyclerView 何时处于滚动状态。
例如,上面是 JankStats 示例中的代码,表明该工具如何检测 RecyclerView
,以向 JankStats 提供此信息:
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView,
newState: Int)
{
val metricsState = metricsStateHolder?.state ?: return
when (newState) {
RecyclerView.SCROLL_STATE_DRAGGING -> {metricsState.addState("RecyclerView", "Dragging")
}
RecyclerView.SCROLL_STATE_SETTLING -> {metricsState.addState("RecyclerView", "Settling")
}
else -> {metricsState.removeState("RecyclerView")
}
}
}
}
此状态可在您利用中的任何中央 (甚至从其余库) 注入,当其报告后果时,会被 JankStats 接管到。这样一来,当您从 JankStats 获取报告时,不仅能够晓得每帧里各种事件破费的工夫,还能够理解用户在那一帧期间做了什么,这可能会是相当有用的信息。
资源
上面这些资源能够帮忙您理解无关 JankStats 的更多信息:
AndroidX 我的项目 : JankStats 位于 AndroidX 的 androidx.metrics 库中。
文档 : 开发者网站提供了新的 开发者指南,其中介绍了 JankStats 的用法。
示例代码 : 示例我的项目 展现了如何将 JankStats 对象实例化并进行侦听,以及如何针对重要的界面状态信息来监测利用。
错误报告 : 若您对该库有任何疑难,或是想提出 API 需要,欢送向咱们 提交错误报告。
Alpha -> 1.0
JankStats 刚刚公布了首个 alpha 版本,这次公布的用意是: “ 咱们认为这个 API 和性能会对 1.0 版本的公布颇有帮忙,请先试用,并和咱们分享您的反馈。”
今后咱们还想针对 JankStats 做其余事件,包含增加某种聚合机制,甚至与现有的上传服务同步。不过,在推出首个版本之前,咱们心愿理解大家的应用状况,以及收集大家想要的其余性能。咱们心愿这一版本在以后的根本状态下能对大家有所帮忙。仅仅是轻松检测并记录界面状态信息这个性能,应该就能够为大家提供一些便当。
当初就请大家 获取 并试用此版本,咱们恭候大家提出的 反馈。最重要的是,咱们心愿大家能借助 JankStats 找出并修复性能问题!您的用户正等着您呢,别让他们等太久了!
欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!