android.cts.jank.ui.CtsDeviceJankUi--testScrolling失败原因总结和debug方法介绍

本文针对CompatibilityTestPackage:com.android.cts.jank2中的android.cts.jank.ui.CtsDeviceJankUi--testScrolling测项,分析了常见的测试失败原因及调试方法,包括帧数不足、帧数为0等问题,并提供了详细的排查步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Compatibility Test Package:com.android.cts.jank2 这个包里面的android.cts.jank.ui.CtsDeviceJankUi--testScrolling测项,经常会遇到

junit.framework.AssertionFailedError: Too few frames received. Monitor: WindowContentFrameStatsMonitorImpl, Expected: 50, Received: xxx. at junit.framework.Assert.fail(Assert.java:50)

上面的xxx,有的时候是一个小于50的数字,有的时候是0  

这个cts 测项的过程和目的是:
在/cts/suite/cts/deviceTests/jank2/src/android/cts/jank/ui/CtsDeviceJankUi.java中启动 com.android.cts.ui/com.android.cts.ui.ScrollingActivity这个activity,然后模拟scroll down滑动list列表,
在list列表滑动动画过程中,统计2s内更新的帧数,是否达到50帧,若未达到50帧则fail

下面介绍这个cts fail的通常原因,还有对应的debug手法


android.cts.jank.ui.CtsDeviceJankUi--testScrolling测项 fail的原因通常有如下几种原因

1.收到的frame count 小于或接近50,如46这样的数字
=>fail原因:滑动listview的时候,因为view system和listview滑动相关的参数被修改了,导致滑动的性能受到影响
=>debug手法:

1)请检查如下2支文件相比于原始版本,做了哪些改动
frameworks/base / core/java/android/view/ViewConfiguration.java 文件中,通常关注 private static final float SCROLL_FRICTION
 frameworks/base / core/java/android/widget/AbsListView.java       文件中,通常关注 private float mVelocityScale
除了上述列出通常关注的两个变量外,其他变量也都要关注,看做了哪些改动

2)请检查LCM 刷新率是否达标?系统performance是否受到影响而降低?=>可观察手机通常的使用场景下,UI刷新是否卡顿,若有要先解决系统performance低的问题


2.收到的frame count为0,Received: 0
=>fail原因:cts 模拟scroll down后,由于客户客制化原因,使得画面更新的区域并不在listview的区域内,导致并未收到listview更新的任何帧,故fail
=>debug手法:
    1)请确认手机是否有硬体按键?检查是否有对Navigationbar的显示做过客制化?若有,请恢复成原始设计
    2)若对第1)条未做改动,那么需要在eng或userdebug版本上,adb命令打开debug开关,查看为何没有更新listview所在区域?

需要打开的debug开关如下:
adb shell setprop debug.viewroot.enable e0002
 adb shell setprop debug.listview.dumpinfo 1111111
 adb shell "stop;start"

然后抓取cts test fail的完整mtklog来分析
分析的时候,主要在com.android.cts.ui.ScrollingActivit的进程中search 如下这样的关键log:
01-01 05:29:46.945 13767 13767 V ViewRootImpl: Invalidate child: Rect(0, 1140 - 720, 1184),this = ViewRoot{e5c0e9b com.android.cts.ui/com.android.cts.ui.ScrollingActivity,ident = 0}
然后观察下面画面刷新的callstack是由谁触发的
01-01 05:29:46.945 13767 13767 V ViewRootImpl: java.lang.Throwable: invalidateChildInParent
01-01 05:29:46.945 13767 13767 V ViewRootImpl: at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1109)

cts 所跑的时间段可以通过如下log来确认:
TestRunner: started: testScrolling(android.cts.jank.ui.CtsDeviceJankUi)
TestRunner: finished: testScrolling(android.cts.jank.ui.CtsDeviceJankUi)
 
3.cts fail的log与frame count无关
例如下面的fail log:
   <FailedScene message="java.lang.reflect.InvocationTargetException&#13;&#10;at java.lang.reflect.Method.invoke(Native Method)&#13;">
 <StackTrace>java.lang.reflect.InvocationTargetException
 at java.lang.reflect.Method.invoke(Native Method)
 at android.support.test.jank.JankTestBase.runTest(JankTestBase.java:116)
 at junit.framework.TestCase.runBare(TestCase.java:134)
 at junit.framework.TestResult$1.protect(TestResult.java:115)
 at android.support.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:77)
 at junit.framework.TestResult.run(TestResult.java:118)
 at android.support.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:55)
 at junit.framework.TestCase.run(TestCase.java:124)
 at android.support.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:63)
 at android.support.test.internal.runner.junit3.AndroidTestSuite$1.run(AndroidTestSuite.java:98)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
 at java.util.concurrent.FutureTask.run(FutureTask.java:237)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
 at java.lang.Thread.run(Thread.java:818)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.test.uiautomator.UiObject2.fling(android.support.test.uiautomator.Direction)' on a null object reference
 at android.cts.jank.ui.CtsDeviceJankUi.testScrolling(CtsDeviceJankUi.java:58)
 ... 15 more
 </StackTrace>

=>fail原因:不明原因的客制化引起cts的测试过程出错,还未跑入该项具体的cts test 流程
=>debug手法:
1)先check view system的几支主要文件是否做过改,然后再请cts framework的人做进一步做分析,因为该结果显示TestRunner都没跑起来
/frameworks/base/core/java/android/view/View.java
 /frameworks/base/core/java/android/view/ViewGroup.java
 /frameworks/base/core/java/android/view/ViewRootImpl.java

2)再检查是否只有这一项cts test fail?根据这种fail log,应该是有非常多的test都会fail,需要从cts test的测试注意事项和流程方面入手分析

<think>我们正在讨论的是Android性能跟踪中ActivityThread$H类的作用及其消息处理机制,特别是trace中出现的`android.app.ActivityThread$H: #159`的含义。 首先,回顾一下ActivityThread$H类: - ActivityThread是Android应用的主线程类,负责管理应用组件的生命周期(如Activity、Service等)以及主线程的消息循环。 - ActivityThread内部有一个名为H的类,它继承自Handler,用于处理系统发送给应用主线程的各种消息。这些消息包括启动Activity、暂停、停止、绑定服务等。 在Android系统中,ActivityThread$H处理的消息类型以整数常量表示,例如: - 100: LAUNCH_ACTIVITY (启动Activity) - 101: PAUSE_ACTIVITY (暂停Activity) - 102: PAUSE_ACTIVITY_FINISHING - 103: STOP_ACTIVITY_SHOW - 104: STOP_ACTIVITY_HIDE - ...等等 那么,消息#159代表什么? 在Android源代码中,我们可以查找ActivityThread.java文件,查看H类中定义的消息常量。由于Android版本不同,消息代码可能会有所变化。根据常见的Android版本(例如Android 10及以上)的源代码,我们可以找到: 在Android 10(API 29)的ActivityThread.java中,H类的消息常量定义如下(部分): ```java public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { ... break; } ... // 其他消息 } } ``` 具体到消息代码159,我们需要查找对应的常量名。在Android 10中,消息159对应的是`RELAUNCH_ACTIVITY`。这个消息用于重新启动一个Activity,通常发生在Activity需要重新启动以处理配置变更(如屏幕旋转)或者被系统杀死后需要恢复时。 在Android 11(API 30)的源代码中,H类的消息常量定义中,159同样对应`RELAUNCH_ACTIVITY`。 因此,在trace中看到`android.app.ActivityThread$H: #159`,意味着主线程正在处理一个重新启动Activity的消息。 **为什么在性能跟踪中这个信息重要?** - 当我们在性能分析(例如使用Traceview或Perfetto)中看到这个trace点,说明系统正在执行Activity的重新启动。这个过程可能涉及资源的重新加载、视图的重新创建等,是一个相对耗时的操作。 - 如果这个操作频繁出现或者执行时间过长,可能会影响应用的性能,导致界面卡顿。 **如何优化?** - 减少不必要的配置变更:例如,如果Activity不需要处理屏幕旋转,可以在AndroidManifest.xml中为Activity设置`configChanges`属性,避免重新创建。 - 优化Activity的启动过程:例如,减少在`onCreate()`方法中的耗时操作,使用异步加载数据等。 **在trace中的表现** 在性能跟踪工具中,你可能会看到类似这样的调用栈: ``` android.app.ActivityThread$H.handleMessage (ActivityThread.java:18xx) [what=159] android.os.Handler.dispatchMessage (Handler.java:106) ... ``` 这表示主线程的消息队列处理到了类型为159(即RELAUNCH_ACTIVITY)的消息。 **总结** `android.app.ActivityThread$H: #159`在trace中表示主线程正在处理重新启动Activity(RELAUNCH_ACTIVITY)的消息。这个操作通常由配置变更(如屏幕旋转)或系统恢复Activity引起,是性能分析中需要关注的点,因为它可能导致界面卡顿。 [^1]: 引用[1]提到了ActivityThread在处理消息(如activityStart)时使用Trace.traceBegin进行跟踪,这有助于我们理解在性能跟踪中ActivityThread$H消息处理的重要性。 [^2]: 引用[2]强调了ActivityThread作为系统进程应用进程通信桥梁的核心作用,进一步说明理解其消息机制的重要性。 §§相关问题§§ 1. 如何识别优化由Activity重新启动引起的性能问题? 2. Android中还有哪些常见的消息类型(如ActivityThread$H中的消息)及其对性能的影响? 3. 如何使用性能分析工具(如Perfetto)跟踪ActivityThread$H的消息处理? 4. 如何避免因配置变更导致的Activity重新创建?</think>### 理解 `android.app.ActivityThread$H: #159` 在性能跟踪中的含义 在 Android 性能跟踪(如 Systrace 或 Perfetto)中,`android.app.ActivityThread$H: #159` 是一个关键标识,它揭示了主线程消息处理机制的核心运作。下面结合引用内容逐步解析: #### 1. **`ActivityThread$H` 的核心作用** - **系统与应用进程的桥梁** `ActivityThread` 是 Android 应用主线程的"大脑",而 `H` 是其内部 Handler 类(继承自 `Handler`)。它负责: - 接收系统进程(如 ActivityManagerService)发送的消息 - 调度应用组件的生命周期(Activity 启动/暂停、Service 绑定等) - 管理主线程消息队列(Looper 机制) - 引用[2]强调:这是**系统进程应用进程通信的桥梁**,也是 Activity 渲染的起点[^2]。 - **消息处理机制** 通过 `handleMessage()` 方法处理消息,消息类型以整数编码(如 `#100` 启动 Activity)。引用[1]展示了典型处理逻辑: ```java Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; // 解析消息对象 // [^1] ``` #### 2. **`#159` 的具体含义** - **消息类型解析** 在 Android 源码中,`#159` 对应常量 `RELAUNCH_ACTIVITY`(不同版本可能略有差异): - **触发场景**:Activity 因配置变更(如屏幕旋转)、资源更新或进程恢复时需要重建 - **关键操作**: ```java case RELAUNCH_ACTIVITY: // #159 handleRelaunchActivity((ActivityClientRecord) msg.obj); // 重建Activity break; ``` - **性能跟踪中的表现** 在 Systrace/Perfetto 中看到此标记,表明主线程正在执行 Activity 重建任务: - 耗时操作:加载布局、初始化资源、恢复状态 - 潜在瓶颈:若此段代码执行时间过长,会导致界面卡顿(Jank)或 ANR #### 3. **性能影响与优化建议** - **常见性能问题** - **资源加载延迟**:如引用[4]提到的 `Resources$NotFoundException`,WebView 内核切换时资源 ID 解析失败[^4] - **序列化瓶颈**:重建时需序列化/反序列化状态数据(`Parcelable`),复杂对象会阻塞主线程 - **配置变更开销**:屏幕旋转等操作触发完整重建 - **优化策略** ```mermaid graph LR A[#159消息处理] --> B{优化方向} B --> C[避免不必要的重建] B --> D[异步加载资源] B --> E[状态序列化优化] ``` - **配置豁免**:在 `AndroidManifest.xml` 中声明 `android:configChanges="orientation|screenSize"` - **异步初始化**:将 WebView/大资源加载移到后台线程 - **轻量化状态**:实现 `Parcelable` 时避免包含 Bitmap 等重型对象 #### 4. **高级调试技巧** - **Hook 消息流**:使用引用[3]提到的工具(如 [Binderceptor](https://github.com/iofomo/binderceptor))拦截系统消息,分析 `#159` 触发频率[^3] - **Trace 注入**:在 `handleRelaunchActivity()` 插入自定义 Trace 标记: ```java Trace.traceBegin(Trace.TRACE_TAG_VIEW, "MyApp:RelaunchActivity"); // 重建代码... Trace.traceEnd(); ``` - **内核选择监控**:如引用[4]所述,通过 `config_webViewPackageName` 检查 WebView 内核切换日志[^4] #### 总结 `android.app.ActivityThread$H: #159` 在性能跟踪中标识主线程正在处理 Activity 重建消息(`RELAUNCH_ACTIVITY`)。它是系统进程与应用通信的关键枢纽[^2],也是界面卡顿的常见源头。优化重点在于减少重建开销、异步化资源加载及状态序列化轻量化。 [^1]: 消息处理逻辑中通过 Trace 标记生命周期起点 [^2]: ActivityThread 作为系统/应用进程桥梁的核心作用 [^3]: 使用 Hook 工具分析系统消息流 [^4]: WebView 资源加载异常与内核切换机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值