第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 稳定性问题的根因分析思路

做根因分析,说白了就是「剥洋葱」。一层一层往下剥,直到找到最根本的原因。我总结了一个四步法:

  1. 收集信息:logcat、kernel log、tombstone、anr trace。缺一不可。
  2. 初步定位:根据现象判断问题发生在哪一层。
  3. 深入分析:针对该层,使用对应工具深挖。
  4. 验证修复:改完代码后,要能复现并验证。

核心原则:永远不要跳过第一步。我见过太多工程师,拿到问题直接猜原因,结果绕了一大圈才发现是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就主动退出。改完后,问题不再复现。

注意:中断上下文里绝对不能做耗时操作。这是内核开发的基本常识。但很多驱动工程师为了省事,把大量逻辑塞进中断处理函数里。这种代码,迟早出事。

好了,这一章的内容就到这里。记住,做稳定性工作,首先要懂系统架构。哪一层出问题,用哪一层的工具。不要拿着锤子看什么都像钉子。下一章,我们来聊聊内存问题的分析方法。