Android 性能优化——ANR监控与解决_waited 5000ms for focusevent(2)

  • 沉默ANR(SilentAnr),SilentANR 会直接kill进程,不会设置该标识。
  • 闪退ANR,OPPO VIVO 的机型ANR之后,会直接闪退,也不会设置该标识。

应对方案:结合主线程的卡顿状态。

反射获取主线程MessageQueuemMessages 对象,此对象的when变量时预期该消息被处理的时间,该变量和当前时间做差就能得到该消息等待的时间,被耽误的耗时,如果该耗过长,就说明主线程‘卡顿’了。

收到了SIGQUIT且当前主线程卡顿了,就认为发生了 ANR。

2.2.3 ANR 监控小结

通过监听系统SIGQUIT信号结合check 当前进程的 NOT_RESPONDING标识和主线程的卡顿状态,综合判定为该进程发生了ANR。

这只是我们知道发生了ANR,知道发生了ANR,进一步知道什么导致了ANR,采集ANR发生时的上下文环境信息,解决ANR更重要。

3 信息的采集监控

3.1 ANR问题定位难点

观察ANR信息采集的难点在于往往信息采集不准确、不全面,当ANR发生的当下采集的信息并不是ANR的真正诱因,因而采集的信息对排查问题的参考价值大折扣。

如上图所示,在主线程耗时的任务已经执行完毕,service启动任务在超过了规定的阈值产生了ANR,此时采集的信息是一个正常的任务调用信息。

总的来说,诱发ANR的原因分主线程执行耗时过大和系统负载过重。

主线程任务执行耗时过大又可大概分为一下几种

  • 历史消息里有多个耗时较重,触发了ANR。
  • 历史消息里有一个耗时极其重的消息。
  • 极其多个耗时小消息执行综合起来耗时严重,触发ANR。

系统负载过重,包括系统内存不足,cpu负载,导致任务得不到执行。

如果能较完整记录过完一段时间段内主线程历史消息任务、当前执任务和将要执行的任务以及系统负载情况,对我们更为准确的诊断ANR问题有非常重要的意义。

3.2 消息调度监控

主线程中记录监控Looper消息执行方案,我们自然的把目光转移到了Looper的Printer方案上。关于这个在三板斧的前两篇文章都介绍过这里不展开。

Looper分发消息执行的时候,前后都打印消息信息,我们依此可以获取到消息任务的相关信息,包活Message的targetwhatcallback、消息执行的耗时等。

消息耗时,需要采集主线程的WallTime和ThreadTime。

  • WallTime:任务占用的时间,包括等待锁,线程休眠时间片流转的时间。
  • ThreadTime(CpuTime)时线程真实执行的时间,不包括等待锁等时间。依次我们可以在侧面推断系统负载情况。

大部分情况下,消息执行都耗时较短,Looper也会有Idel状态,即无消息执行的状态,我们需要对这些消息进行聚合处理。

此外,三板斧系列文章主线程耗时监控里有介绍主线程处理消息除了Looper正常的分发的消息需要监控外,IdleHandler、TouchEvent消息也要纳入到统计记录里才更为完整。

3.2.1 消息聚合以及分类
  • 连续的耗时较小的消息聚合统计,连续小于50ms 的消息,聚合成一条记录,该记录里存储消息的数量,和总耗时信息等
  • 超过阈值的消息单做一条记录统计。
  • 系统调用消息统计(ActivityThread.H Activity 、Service、 ContentProvider),这些对我们分析ANR问题很重要。
  • IDLE状态消息单独统计。
  • IdleHandler 的统计。
  • TouchEvent 等native 层触发的主线程调度任务统计。

综合来说,把消息分类型,聚合类型(Agree),连续的耗时较少的消息。耗时类型(Huge):超过50ms的消息。系统调用消息(SYSTEM)

3.2.2 消息堆栈采集

除了,统计记录Looper Messge的what、callback和耗时之外,每个消息内到底执行了什么哪些动作也需要采集,这就需要采集每个消息的执行的栈,频繁的采集执行栈对性能影响较大,要进行有策略的采集。

  • 开启子线程采集主线程的堆栈。
  • 耗时较少的消息任务不采集。
  • 超过一定阈值还未执行完毕的消息任务进行一次采集,再隔一段时间如果本次消息任务还未执行完,再进行采集,间隔时间是依此线性增加。
  • 【shoppe的非堵塞式高效抓栈】
3.2.3 正在执行的消息和pending消息统计

除了监控 ANR 发生之前主线程历史消息调度及耗时之外,也需要知道 ANR 发生时正在调度的消息及其耗时,以便于在看到 ANR 的 Trace 堆栈时,可以清晰的知道当前 Trace 逻辑到底执行了多长时间

MessageQueue中等待执行的消息也很有必要统计

  • 对我们知道什么组件诱发了ANR的产生
  • 可以统计等待执行的消息等待了多久。判断消息队列繁忙程度。
3.3 更全面的信息采集

以上我们比较全面的监控统计主线程调度任务的耗时,

3.3.1 获取ANRInfo

应用层可通过AcivityManager获取ANRInfo

val am = application.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val processesInErrorStates = am.processesInErrorState

ProcessesInErrorState中可获取到

shortMsg: ANR Inputdispatching timed out …

  • shortMessage:ANR 产生的直接原因,TouchEvent超时,Servcie启动超时等。

Reason: Input dispatching timed out
xxx is not responding. Waited 5003ms for FocusEvent(hasFocus=false))
Load: 3.19 / 2.46 / 2.42

----- Output from /proc/pressure/memory -----
some avg10=0.00 avg60=0.00 avg300=0.00 total=144741615
full avg10=0.00 avg60=0.00 avg300=0.00 total=29370150
----- End output from /proc/pressure/memory -----

CPU usage from 0ms to xxx ms later with 99% awake:

TOTAL: 6.4% user + 8.2% kernel + 0% iowait + 0.6% irq + 0.1% softirq

CPU usage from 0ms to xxx ms later with 99% awake:

27% TOTAL: 10% user + 14% kernel + 0.3% iowait + 0.9% irq + 0.3% softirq

  • longMessage:包括系统负载,cup使用情况,IO情况等系统情况。
3.3.2 Logcat 日志

应用运行时采集logcat日志,注意需要控制量有相应的策略,定时清理。

String cmd = "logcat -f " + file.getAbsolutePath();
Runtime.getRuntime().exec(cmd);

3.3.3 当前进程其他线程的堆栈信息

从 Java 层获取各线程堆栈,或通过反射方式获取到虚拟机内部 Dump 线程堆栈的接口,在内存映射的函数地址,强制调用该接口,并将数据重定向输出到本地。

这样当ANR发生时我们就有丰富的信息供我们参考,包括主线程过去现在和未来的调度信息,系统的信息,线程信息,Logcat信息。

4 问题分析定位和解决

4.1 分析主线程调度情况

查看我们主线程任务调度,是否有明显的耗时任务执行,诱发ANR产生。

  • 分析我们记录的消息调度的wallTime和cputime
  • 定位记录的耗时消息堆栈定位出问题
  • 注意有可能出现连续的非常多的耗时小的消息也会造成ANR
4.2 ANR Info解读
4.2.1 系统负载情况

Load: xx/xx/xx

ANR发生的前1分钟 5分钟和15分钟时间段内的CPU负载,数值代表着等待系统调度的任务数,数值过高,意味着系统有CPU和IO竞争激烈,我们的应用进程可能收到影响

4.2.2 CPU占用情况

CPU usage from 0ms to xxx ms later with xx% awake:

14% 1673/system_server: 8% user + 6.7% kernel / faults: 12746 minor
13% 30829/tv.danmaku.bili: 7.3% user + 6.2% kernel / faults: 24286 minor
6.6% 31147/tv.danmaku.bili:ijkservice: 3.7% user + 2.8% kernel / faults: 11880 minor
6% 574/logd: 2.1% user + 3.8% kernel / faults: 64 minor

TOTAL: 6.4% user + 8.2% kernel + 0% iowait + 0.6% irq + 0.1% softirq

CPU usage from xxms to xxxms later
73% 1673/system_server: 49% user + 24% kernel / faults: 1695 minor
33% 2330/AnrConsumer: 12% user + 21% kernel
15% 1683/HeapTaskDaemon: 15% user + 0% kernel
9.2% 7013/Binder:1673_12: 6.1% user + 3% kernel
6.1% 1685/ReferenceQueueD: 6.1% user + 0% kernel
6.1% 2715/HwBinder:1673_5: 6.1% user + 0% kernel
3% 2529/PhotonicModulat: 0% user + 3% kernel
25% 30829/tv.danmaku.bili: 4.2% user + 21% kernel / faults: 423 minor
25% 31050/thread_ad: 4.2% user + 21% kernel


27% TOTAL: 10% user + 14% kernel + 0.3% iowait + 0.9% irq + 0.3% softirq

如上,表示ANR发生前后的CPU占用情况,以及这些进程具体占用情况。

  • user:用户态
  • kernel:内核态
  • iowait:io等待。过高可能发生文件读写或内存紧张的情况。
  • irq:硬中断
  • softirq:软中断 占比

注意:单进程CPU的负载并不是以100%为上限,而是有几个核,就有百分之几百,如8核上限为800%

另外,kswapdmmcqd系统关键线程CPU线程过大,往往伴随系统回收资源,影响到应用进程

通过对ANR信息的解读可以更佳全面的帮助我们定位ANR问题。

4.3 Logcat消息

线上如果记录了Logcat打印消息,着重从以下方面去看问题

  • onTrimeMemory:连续的onTrimMemory 往往说明APP的内存不足可能系统资源不足造成ANR
  • Slow operation Slow delivery 出现该情况系统性能受限。

5 结

从ANR产生的原因,系统处理ANR,应用层监听ANR,应对ANR应用侧比较全面的监控主线程的任务调度以及为了解决ANR采集系统信息最后给出了分析解决ANR问题的总体思路。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-NlAl3FeR-1712645288810)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

<think>我们正在讨论一个Android开发中的具体问题:LaunchActivity (server)无响应,且等待5000ms获取FocusEvent(hasFocus=false)的问题。 根据问题描述,这通常发生在启动Activity时,系统在等待焦点事件(FocusEvent)时超时(5000ms),并且hasFocus=false表示Activity没有获得焦点。 可能的原因和解决方法如下: 1. **主线程阻塞**:如果主线程(UI线程)在执行耗时操作,可能导致无法及时处理焦点事件。 - 解决方法:确保主线程不执行耗时操作,将耗时任务移到子线程(如使用AsyncTask、HandlerThread、ThreadPoolExecutor等)或使用协程(Kotlin)。 2. **布局过于复杂**:如果Activity的布局层次太深或包含复杂的View,可能导致测量、布局和绘制过程耗时过长。 - 解决方法:优化布局,减少布局层级(使用ConstraintLayout等),避免过度绘制。 3. **同步的IPC调用**:在主线程中执行了同步的跨进程调用(IPC),如ContentResolver请求、Binder调用等,这些操作可能阻塞主线程。 - 解决方法:避免在主线程进行同步IPC,改用异步方式或在工作线程执行。 4. **Activity启动时初始化过多**:在Activity的onCreate()等方法中执行了过多的初始化操作。 - 解决方法:延迟初始化(使用ViewStub、懒加载等),将非必要的初始化放到后台线程或需要时再加载。 5. **焦点处理问题**:可能是由于焦点在某个View上无法正常转移,导致FocusEvent无法正常传递。 - 解决方法:检查布局中View的焦点设置(如focusable, focusableInTouchMode等),确保焦点可以正常传递。 6. **系统资源紧张**:系统资源不足(如内存、CPU)导致处理事件延迟。 - 解决方法:优化应用内存使用,避免内存泄漏,减少资源占用。 7. **第三方库或自定义View的问题**:第三方库或自定义View可能存在性能问题或死锁。 - 解决方法:检查是否有自定义View的绘制或事件处理耗时过长,或者使用工具(如Systrace)分析卡顿点。 8. **使用工具分析**: - **Systrace**:使用Systrace工具跟踪系统调用,定位卡顿点。 - **ANR日志**:查看/data/anr/traces.txt文件(需要root权限)或通过Android Studio的Logcat过滤ANR信息,分析堆栈。 9. **检查是否在onWindowFocusChanged方法中有耗时操作**:当Activity获得焦点时会回调onWindowFocusChanged,如果在此方法中执行耗时操作也会导致问题。 - 解决方法:确保onWindowFocusChanged中只执行轻量级操作。 10. **检查Activity启动模式(LaunchMode)**:如果Activity的启动模式设置不当(例如singleTask等),可能会引发一些意外的问题。 - 解决方法:检查AndroidManifest.xml中Activity的launchMode设置,尝试调整为标准模式(standard)测试。 由于具体问题可能因代码而异,建议使用上述工具进行性能分析,定位瓶颈。 相关问题: 1. 如何优化Android应用的启动时间? 2. 如何使用Systrace分析Android性能问题? 3. 如何避免Android应用的主线程阻塞? 4. 如何分析Android ANR日志? 5. 在Android中,焦点事件的处理机制是怎样的? </think>针对 LaunchActivity (server) 无响应且等待 5000ms 获取 `FocusEvent(hasFocus=false)` 的问题,以下是系统级解决方案和排查步骤: ### 问题原因分析 1. **焦点传递阻塞** Activity 启动时窗口管理器(WindowManagerService)等待焦点事件超时,`hasFocus=false` 表示焦点未正确传递到目标窗口,通常由主线程阻塞或系统资源争用导致[^WM]。 2. **关键触发点** - `ViewRootImpl#handleAppVisibility()` 中等待 `FocusEvent` 超时 - `WindowManagerService` 在 `addWindow()` 时未收到焦点回调 - 主线程被阻塞导致 `InputDispatcher` 事件分发延迟[^AMS] --- ### 解决方法 #### 1. **主线程优化** ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 将耗时操作移至后台线程 CoroutineScope(Dispatchers.Default).launch { initHeavyWork() // 耗时初始化 } } ``` - 检查 `onCreate()`/`onResume()` 中的同步 IPC、数据库操作或复杂计算 - 使用 `StrictMode` 检测主线程阻塞: ```java StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads().detectNetwork().penaltyLog().build()); ``` #### 2. **窗口焦点配置** - **禁用非必要焦点**:在根布局添加 ```xml <LinearLayout android:focusable="true" android:focusableInTouchMode="true"> ``` - **检查嵌套焦点冲突**: 使用 `adb shell dumpsys window windows` 确认焦点窗口 ID #### 3. **系统级修复** - **调整窗口超时参数(需 root)**: ```bash adb shell settings put global window_input_event_timeout 8000 # 延长超时阈值 ``` - **关闭 MIUI/OxygenOS 等定制 ROM 的窗口优化**: 开发者选项 → 停用「HW叠加层」和「强制GPU渲染」 #### 4. **ANR 日志分析** 检查 `/data/anr/traces.txt` 关键信息: ```log "main" prio=5 tid=1 Blocked | group="main" sCount=1 dsCount=0 flags=1 obj=0x72e8f7a0 | sysTid=3021 waiting on condition <0x0f2c2660> at android.view.ViewRootImpl.waitForFocusEvent(ViewRootImpl.java:3876) ``` 重点关注 `Binder` 调用栈和 `InputDispatcher` 阻塞点[^ANR] --- ### 验证流程 1. 使用 **Systrace** 定位卡顿: ```bash python systrace.py --time=5000 gfx view wm am ``` ![Systrace焦点事件延迟示例](https://developer.android.com/static/images/tools/systrace_wm.png) *检查 `WaitForFocusEvent` 轨迹长度*[^Systrace] 2. 最小化复现: 在 `BaseActivity` 中重写 `onWindowFocusChanged()` 添加日志: ```java public void onWindowFocusChanged(boolean hasFocus) { Log.d("FOCUS_DEBUG", "FocusEvent: " + hasFocus); // 若此处未触发,说明窗口管理器异常 } ``` --- ### 扩展建议 - **系统级 Hook**:通过 Xposed 模块拦截 `WindowManagerService.addWindow()` 调用 - **ROM 兼容性**:在华为/小米设备上关闭「内存加速」和「应用自启动」限制 相关问题: 1. 如何解读 Android ANR 日志中的 `InputDispatcher` 超时? 2. Systrace 分析中哪些标签能辅助诊断窗口焦点问题? 3. 定制 ROM 下如何处理系统级的主线程阻塞? 4. 窗口管理器(WMS)的焦点传递机制是怎样的? 5. 如何通过 Binder 调用分析跨进程焦点事件传递? [^WM]: Android WindowManager 源码分析 - 《深入理解Android内核设计思想》 [^AMS]: ActivityManagerService 事件调度机制 - Android AOSP 文档 [^ANR]: Android ANR 诊断指南 - Google Developers [^Systrace]: Systrace 性能优化实战 - Android 官方培训
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值