尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android ANR问题解析与优化实践

Android ANR问题解析与优化实践 1. ANR问题概述为什么你的Android应用会无响应作为一名Android开发者最不愿看到的就是应用弹出Application Not Responding的对话框。ANR不仅直接影响用户体验更是应用质量的重要指标。根据Google Play的数据统计ANR问题导致的用户流失率高达47%远超过其他类型的崩溃。ANR的触发机制其实很简单当应用的主线程UI线程被阻塞超过5秒或者BroadcastReceiver在10秒内没有完成执行系统就会判定为ANR。但看似简单的定义背后隐藏着复杂的成因主线程阻塞最常见的ANR原因通常由主线程执行耗时操作引起如网络请求、数据库操作、复杂计算等死锁多线程同步不当导致线程相互等待系统资源紧张CPU、内存、I/O等资源不足导致响应延迟Binder通信超时跨进程通信耗时过长提示从Android 11开始系统会为每个ANR生成独立的trace文件存放在/data/anr目录下这是排查ANR问题的第一手资料。2. ANR排查工具链从基础到进阶2.1 必备基础工具Android Studio的Profiler套件是最基础的排查工具特别是其中的CPU Profiler和Memory Profiler。但很多开发者不知道的是在Android Studio 4.1之后Profiler新增了System Trace功能可以捕获系统级的线程调度信息。adb logcat命令仍然是获取实时日志的利器。建议使用以下参数组合adb logcat -v threadtime -b main -b system -b crash这个命令会输出带有精确时间戳的日志并包含main、system和crash三个最重要的日志缓冲区。2.2 高级分析工具对于复杂的ANR问题需要更专业的工具systrace系统级性能分析工具可以可视化线程调度和CPU使用情况python systrace.py -a your.package.name -o trace.html sched freq idle am wm gfx view binder_driverPerfettoGoogle新一代性能分析工具支持更长的跟踪时间和更丰富的数据类型adb shell perfetto --txt -c /data/misc/perfetto-configs/trace_config.pbtxt -o /data/misc/perfetto-traces/trace.perfetto-traceStrictMode开发阶段预防ANR的利器可以检测主线程的磁盘和网络操作StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build());3. ANR日志深度解析从表面现象到根因定位3.1 关键日志字段解读一份完整的ANR日志通常包含以下关键信息----- pid 12345 at 2023-07-20 14:30:00 ----- Cmd line: com.example.app Build: Android 13/TP1A.220624.014 DALVIK THREADS (12): main prio5 tid1 Native | groupmain sCount1 dsCount0 flags1 obj0x71e8f6a8 self0x7c8e4a4000 | sysTid12345 nice-10 cgrpdefault sched0/0 handle0x7c906f84f0 | stateS schedstat( 123456789 987654321 123 ) utm12 stm34 core1 HZ100 | stack0x7fc3251000-0x7fc3253000 stackSize8192KB | held mutexes at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:339) at android.os.Looper.loop(Looper.java:183) at android.app.ActivityThread.main(ActivityThread.java:8026) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)关键字段解析prio线程优先级tid线程IDstate线程状态S表示休眠R表示运行D表示不可中断休眠schedstat调度统计运行时间、等待时间、切换次数stack调用栈最关键的诊断信息3.2 典型ANR模式识别通过分析数百个ANR案例我总结出几种典型模式模式1主线程阻塞main prio5 tid1 Blocked at java.lang.Object.wait(Native Method) at com.example.app.DataManager.loadData(DataManager.java:123)模式2Binder通信超时Binder transaction to com.android.systemui timed out模式3资源竞争AsyncTask #1 prio5 tid12 Blocked at com.example.app.CacheManager.put(CacheManager.java:45) - waiting to lock 0x12345678 held by thread 14. 时间线分析法还原ANR发生现场4.1 构建时间线的基本方法时间线分析的核心是将所有相关事件按时间顺序排列。具体步骤从ANR日志中提取时间戳如ANR发生时间收集前后各10秒的logcat日志获取对应时间的systrace或Perfetto记录关联应用内自定义的日志时间戳4.2 实战案例解析假设我们遇到一个图片加载导致的ANR时间线分析如下时间戳事件线程耗时14:29:55.000用户点击图片列表项main-14:29:55.100开始加载高清图片main100ms14:29:57.300图片解码完成bg-thread2200ms14:30:00.000系统检测到ANR--从这个时间线可以清晰看出主线程在图片加载时被阻塞了2.2秒加上其他操作累计超过5秒触发了ANR。4.3 高级时间线技巧时钟同步确保设备日志、应用日志和服务端日志的时间一致关键路径标记在代码中插入关键点日志long startTime SystemClock.uptimeMillis(); // 执行操作 Log.d(PERF, Operation took (SystemClock.uptimeMillis() - startTime) ms);可视化工具使用Chrome的trace viewerchrome://tracing分析systrace文件5. 常见ANR场景与解决方案5.1 主线程I/O操作错误示例public String loadData() { File file new File(getFilesDir(), data.json); // 主线程读取大文件 return readFileToString(file); }解决方案使用AsyncTask或Loader已废弃推荐使用Kotlin协程viewModelScope.launch(Dispatchers.IO) { val data loadDataFromFile() withContext(Dispatchers.Main) { updateUI(data) } }5.2 过度同步锁错误示例public class CacheManager { private static final Object lock new Object(); public void put(String key, Object value) { synchronized (lock) { // 复杂操作 Thread.sleep(1000); // 模拟耗时 } } }解决方案减小锁粒度使用读写锁ReentrantReadWriteLock考虑无锁数据结构5.3 广播接收器超时错误示例public class MyReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { // 执行耗时操作 processIntent(intent); // 耗时超过10秒 } }解决方案使用goAsync()延长处理时间public void onReceive(Context context, Intent intent) { final PendingResult result goAsync(); new Thread(() - { processIntent(intent); result.finish(); }).start(); }改为使用JobScheduler或WorkManager6. ANR监控与预防体系6.1 线上监控方案成熟的ANR监控应包含以下维度基础信息ANR发生时间、设备型号、系统版本等堆栈分析主线程堆栈、CPU使用情况、内存状态关联分析与崩溃、OOM等其他异常的关联性推荐使用以下开源方案组合Matrix腾讯开端的APM框架提供ANR监控能力xCrash爱奇艺开端的崩溃捕获库支持ANR检测Firebase CrashlyticsGoogle的崩溃分析服务6.2 开发阶段预防自动化测试android { testOptions { execution ANDROIDX_TEST_ORCHESTRATOR animationsDisabled true } }在gradle配置中禁用动画可以更早发现ANR问题压力测试adb shell am instrument -w -r -e debug false -e class com.example.app.StressTest \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner性能基线建立关键路径的性能基线在CI流程中加入性能回归检测7. 疑难ANR案例分析7.1 隐式广播导致的ANR现象应用在后台频繁发生ANR但主线程堆栈显示空闲状态分析过程检查logcat发现大量BroadcastQueue: Background execution not allowed警告发现应用注册了ACTION_BATTERY_CHANGED等系统广播系统广播在后台频繁触发导致应用被唤醒解决方案避免使用隐式广播使用JobScheduler替代轮询需求动态注册广播在适当时机注销7.2 SurfaceFlinger导致的ANR现象UI操作卡顿ANR日志显示Waiting for GPU completion分析过程systrace显示RenderThread长时间阻塞发现使用了复杂的PathEffect和ShaderGPU渲染命令队列积压解决方案简化自定义View的绘制逻辑使用硬件层Hardware Layer分批处理复杂绘制操作在实际项目中ANR问题的排查往往需要结合多种工具和方法。我建议建立一个标准化的排查流程从日志分析入手结合性能工具验证假设最后通过代码修改和测试确认解决方案。记住预防胜于治疗良好的架构设计和编码习惯才是避免ANR的根本之道。
返回列表