第2章:Android系统架构与稳定性
大家好,我是你们的讲师。今天我们来聊聊Android系统架构与稳定性这个话题。说实话,很多工程师做了好几年稳定性工作,却连系统长什么样都说不清楚。这就像修车却不知道发动机在哪,对吧?
2.1 Android系统分层架构
Android系统从下往上,一共四层:内核层、运行时层、框架层、应用层。我习惯把这四层比作一栋楼——地基、管道、房间、住户。哪一层出问题,整栋楼都不好住。
2.1.1 应用层
这是用户直接接触的一层。说白了,就是手机上那些图标。应用层的问题最常见,比如闪退、ANR、内存泄漏。我遇到过最离谱的一次,某个App在后台疯狂申请锁,导致系统直接睡死过去。
2.1.2 框架层
框架层是Android系统的「骨架」。ActivityManagerService、WindowManagerService这些大管家都在这里。框架层出问题,往往表现为系统服务崩溃、界面卡死。嗯,这里有个坑——框架层的死锁特别难查,我曾经花了一周才定位到一个Binder调用超时的问题。
2.1.3 运行时层
运行时层包括ART虚拟机、核心库。你写的Java代码最终都在这里跑。运行时层的问题通常和GC、JIT编译有关。我记得有一次,某个低端机型频繁GC导致系统卡顿,最后发现是某个三方库在疯狂创建临时对象。
2.1.4 内核层
内核层是Android的根基。Linux内核、硬件驱动、电源管理都在这里。内核层出问题,后果最严重——死机、重启、黑屏。我做过一个项目,某款手机的WiFi驱动有内存泄漏,运行三天后必死机。这种问题,应用层工程师根本无从下手。
2.2 各层对稳定性的影响
不同层的问题,表现和影响范围完全不同。我整理了一张表,方便大家对照:
| 层级 | 常见问题 | 影响范围 | 排查难度 |
|---|---|---|---|
| 应用层 | 闪退、ANR、OOM | 单个应用 | 低 |
| 框架层 | 系统服务崩溃、死锁 | 多个应用/系统 | 中 |
| 运行时层 | GC问题、JIT崩溃 | 整个系统 | 高 |
| 内核层 | 死机、重启、驱动崩溃 | 整个设备 | 极高 |
你想想看,应用层的问题最多,但最好修。内核层的问题最少,但一出就是大事。所以做稳定性工作,要「抓大放小」——优先解决内核和框架层的问题。
2.3 稳定性问题的根因分析思路
做根因分析,说白了就是「剥洋葱」。一层一层往下剥,直到找到最根本的原因。我总结了一个四步法:
- 收集信息:logcat、kernel log、tombstone、anr trace。缺一不可。
- 初步定位:根据现象判断问题发生在哪一层。
- 深入分析:针对该层,使用对应工具深挖。
- 验证修复:改完代码后,要能复现并验证。
核心原则:永远不要跳过第一步。我见过太多工程师,拿到问题直接猜原因,结果绕了一大圈才发现是log没抓全。
2.3.1 从log入手
log是稳定性分析的第一手资料。我个人习惯先看这几个地方:
- main log:应用和框架层的日志
- system log:系统服务的日志
- kernel log:内核和驱动的日志
- crash log:tombstone和anr trace
举个例子,有一次某个设备频繁重启。我一看kernel log,发现是看门狗超时。再往下查,发现是某个驱动在中断上下文里做了耗时操作。你看,从log到根因,三步就搞定了。
2.3.2 区分问题类型
稳定性问题大致分三类:
- 内存问题:OOM、内存泄漏、内存碎片
- 并发问题:死锁、竞态条件、锁竞争
- 资源问题:文件描述符泄漏、Binder耗尽、线程耗尽
我曾经遇到一个死锁问题,四个线程互相等待,系统直接卡死。用pstack一看,每个线程都在等不同的锁。这种问题,光看log是不够的,得用工具分析锁的依赖关系。
小技巧:遇到死锁问题,可以用 dumpsys window 或者 dumpsys activity 看看系统服务是否还在响应。如果所有服务都卡住了,大概率是死锁。
2.3.3 工具链的使用
做稳定性分析,工具就是你的武器。我常用的工具有这些:
- strace:跟踪系统调用,适合分析应用行为
- perf:性能分析,适合定位CPU热点
- ftrace:内核跟踪,适合分析驱动和调度问题
- AddressSanitizer:内存错误检测,适合找内存越界
嗯,这里要提醒一下。工具虽好,但不能乱用。我见过有人用perf去分析IO问题,结果折腾半天没找到原因。选对工具,事半功倍。
2.4 实战案例:一次系统重启的根因分析
最后,我分享一个真实案例。某款手机在播放视频时频繁重启。现象是:播放10分钟左右,屏幕一黑,然后重启。
第一步,抓log。从kernel log里看到:
[ 1234.5678] watchdog: BUG: soft lockup - CPU#0 stuck for 22s!
[ 1234.5679] Modules linked in: msm_vidc_decoder
[ 1234.5680] Call trace:
[ 1234.5681] msm_vidc_decoder_irq_handler+0x100/0x200
看到没?软锁死在视频解码驱动的中断处理函数里。这说明驱动在中断上下文里做了太多事情,导致CPU被长时间占用。
第二步,查代码。我找到 msm_vidc_decoder_irq_handler 这个函数,发现里面有个循环,在等待某个硬件寄存器置位。如果硬件出问题,这个循环就永远退不出来。
第三步,修方案。加一个超时机制,如果等待超过100ms就主动退出。改完后,问题不再复现。
注意:中断上下文里绝对不能做耗时操作。这是内核开发的基本常识。但很多驱动工程师为了省事,把大量逻辑塞进中断处理函数里。这种代码,迟早出事。
好了,这一章的内容就到这里。记住,做稳定性工作,首先要懂系统架构。哪一层出问题,用哪一层的工具。不要拿着锤子看什么都像钉子。下一章,我们来聊聊内存问题的分析方法。