一、ANR 核心信息提取
首先从日志中定位关键上下文,明确问题基本属性:
| 关键维度 | 具体信息 |
|---|---|
| ANR 类型 | Input dispatching timed out(输入事件分发超时) |
| 目标进程 | com.mytech.myapp(PID: 7214,UID: 1000,非前台进程:Foreground: No) |
| 目标页面 | com.mytech.myapp/.ui.page.music.MusicPlayerActivity |
| 核心异常点 | ActivityRecord 无焦点窗口(does not have a focused window) |
二、输入分发超时(无焦点窗口)的根本原因分析
Android 输入事件分发(InputDispatcher)的核心逻辑是:仅向前台有焦点的窗口(Focused Window)分发输入事件。后台 Activity 出现该 ANR,本质是「系统错误地向无焦点窗口分发事件」+「事件处理链路阻塞」的叠加问题,具体拆解为以下 4 点:
1. 核心矛盾:后台 Activity 窗口状态异常(未正确失焦 / 销毁)
正常情况下,Activity 切换到后台时(onStop 生命周期),系统会通过 WindowManager 将其窗口标记为「非焦点」并从「输入接收窗口列表」中移除。但日志显示:
- 该 Activity 虽为后台进程(Foreground: No),但 UID=1000(系统权限) :拥有系统权限的应用可能绕过部分窗口管理逻辑,导致窗口未被正确隐藏或失焦。
- InputDispatcher 仍尝试向其分发事件:说明 WindowManager 中该 Activity 的窗口状态未同步为「非焦点」,或 Input 事件队列中残留了针对该窗口的事件,最终分发时发现无焦点窗口而超时。
可能场景:
- Activity 切换后台时,
onPause()/onStop()生命周期执行异常(如被主线程阻塞),未触发窗口失焦逻辑。 - 音乐播放场景下,Activity 可能通过「后台播放服务」保持窗口资源(如封面渲染),但未正确告知 WindowManager 窗口无需接收输入。
2. 系统级阻塞:system_server 高 CPU 占用导致 InputDispatcher 调度延迟
system_server 是 Android 系统核心进程(负责 InputDispatcher、ActivityManager、WindowManager 等核心服务),日志中其 CPU 占用高达 63%(36% user + 26% kernel) ,直接导致:
- InputDispatcher 线程(sysTid=883,属于 system_server)无法及时调度:即使事件本身无需处理,InputDispatcher 也需耗时从队列中筛选「目标窗口」,高 CPU 导致该筛选过程超时。
- 佐证:系统总 CPU 使用率仅 30%(13% user + 15% kernel),但
system_server独占 63%,说明系统服务内部存在资源争抢(如 ActivityManager 频繁刷新进程状态)。
3. 目标进程 RenderThread 阻塞:主线程等待 GPU 渲染导致无响应
目标进程(7214)的 RenderThread(sysTid=7335) 处于 D 状态(不可中断睡眠) ,栈信息显示卡在 GPU 纹理上传 操作:
native: #12 pc 00000000000c0490 /vendor/lib64/egl/libGLESv2_adreno.so (glTexSubImage2D+144)
native: #13 pc 00000000005d1420 /system/lib64/libhwui.so (GrGLGpu::uploadTexData(...))
- RenderThread 是负责 UI 渲染的子线程,卡在
glTexSubImage2D(纹理数据上传到 GPU),说明音乐封面等图像资源在后台仍持续渲染,且 GPU 资源不足(如其他进程占用 GPU)。 - 主线程(sysTid=7214)处于 S 状态(睡眠) ,等待 RenderThread 完成渲染(栈信息:
HardwareRenderer.setStopped→ViewRootImpl.performDraw):主线程无法处理任何消息(包括 InputDispatcher 发来的「事件拒绝」响应),最终触发超时。
4. 第三方进程干扰:com.mytech.navi 高 CPU 占用抢占资源
日志中 com.mytech.navi(PID:16720)CPU 占用达 36%(16% user + 19% kernel) ,该进程可能是车载导航类应用(从包名「autonavi」推测),其高 CPU 占用会:
- 抢占 CPU 时间片:导致目标进程(7214)的主线程 / RenderThread 无法及时调度。
- 可能占用 GPU 资源:导航应用通常需频繁渲染地图,与 MusicPlayerActivity 的封面渲染争抢 GPU,间接导致 RenderThread 阻塞。
三、解决方案与优化建议
针对上述原因,分「应用层优化」和「系统层排查」两维度给出方案:
1. 应用层:修复 MusicPlayerActivity 窗口与生命周期管理
(1)确保后台时窗口正确失焦 / 暂停渲染
-
在
onPause()/onStop()中主动暂停 UI 渲染:@Override protected void onStop() { super.onStop(); // 1. 暂停音乐封面等图像渲染(如 Glide/Picasso 暂停加载) Glide.with(this).pauseRequests(); // 2. 通知 WindowManager 窗口失焦(系统权限应用需额外处理) if (getWindow() != null) { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().setFocusable(false); getWindow().setFocusableInTouchMode(false); } // 3. 停止后台不必要的绘制任务(如自定义 View 的 invalidate()) if (musicCoverView != null) { musicCoverView.stopAnimation(); } } @Override protected void onResume() { super.onResume(); // 恢复时重新启用焦点和渲染 if (getWindow() != null) { getWindow().setFocusable(true); getWindow().setFocusableInTouchMode(true); } Glide.with(this).resumeRequests(); }
(2)避免主线程等待 RenderThread
-
将耗时的图像处理(如封面裁剪、模糊)移到 子线程,避免阻塞主线程:
// 错误:主线程处理图像 Bitmap blurredCover = BlurUtils.blur(originalBitmap); // 耗时操作,阻塞主线程 // 正确:子线程处理 + 主线程更新 new AsyncTask<Void, Void, Bitmap>() { @Override protected Bitmap doInBackground(Void... voids) { return BlurUtils.blur(originalBitmap); // 子线程执行 } @Override protected void onPostExecute(Bitmap result) { musicCoverView.setImageBitmap(result); // 主线程更新 UI } }.execute();
2. 系统层:降低 system_server 与第三方进程资源占用
(1)排查 system_server 高 CPU 原因
- 通过
adb shell top -p 439实时观察system_server内部线程占用,定位高耗线程(如 ActivityManager、WindowManager)。 - 检查系统日志(
adb logcat -s ActivityManager WindowManager),看是否存在「频繁进程切换」「窗口状态刷新异常」(如反复创建 / 销毁窗口)。
(2)限制 com.mytech.navi 资源占用
- 若该导航应用为同厂商应用,优化其 GPU 渲染逻辑(如减少地图纹理更新频率)。
- 若为第三方应用,通过系统权限限制其 CPU 优先级(
adb shell renice 10 16720),避免抢占核心进程资源。
3. 通用优化:输入事件分发防护
-
为后台 Activity 拦截输入事件:在
onWindowFocusChanged(boolean hasFocus)中处理,避免事件传递到主线程:@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (!hasFocus) { // 后台时,拦截所有输入事件 getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } }
四、总结
本次 ANR 的本质是「窗口状态异常 + 系统资源争抢」的叠加:
- 系统权限应用的后台窗口未正确失焦,导致 InputDispatcher 错误分发事件;
system_server与导航应用高 CPU 占用,导致 InputDispatcher 调度延迟;- 目标进程 RenderThread 卡在 GPU 渲染,主线程无法响应事件分发。
通过「修复窗口生命周期管理」「降低系统资源占用」「拦截后台输入事件」三方面优化,可彻底解决该问题。