第七章 ActivityManagerService(AMS):Android系统的“大管家”
说实话,在Android Framework里,AMS是我个人觉得最“有灵魂”的一个服务。它不像Binder那样只是个通信工具,也不像SurfaceFlinger那样只管画图。AMS管的是啥?它管的是你手机上每一个App的“生老病死”——启动、运行、切换、销毁,全在它手里。
我刚开始啃AMS源码的时候,说实话有点懵。代码量太大了,光一个AMS.java就上万行。但后来我找到了一个方法:别把它当代码看,把它当“操作系统”看。AMS本质上就是一个进程和组件的调度中心。今天我们就来拆开它,看看它到底是怎么工作的。
核心要点:AMS是Android系统中管理Activity、Service、BroadcastReceiver、ContentProvider以及进程优先级的核心服务。它运行在SystemServer进程中,是Framework层的“大脑”。
7.1 AMS启动流程:从SystemServer到世界主宰
AMS是怎么启动的?嗯,这得从SystemServer说起。SystemServer是Zygote孵化出的第一个Java进程,它负责启动所有系统服务。
在SystemServer的run()方法里,会调用startBootstrapServices()、startCoreServices()、startOtherServices()这几个方法。AMS就是在startBootstrapServices()里被创建的。
// 简化后的AMS启动流程
private void startBootstrapServices() {
// 1. 创建AMS实例
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class);
// 2. 设置系统进程
mActivityManagerService.setSystemProcess();
// 3. 安装系统Provider
mActivityManagerService.installSystemProviders();
}
这里有个细节我想提一下。AMS的构造函数里会创建mHandlerThread,也就是AMS自己的消息循环线程。为什么需要这个?因为AMS要处理大量的跨进程调用,如果都在主线程里搞,系统UI就卡死了。我在项目里遇到过一个问题:某个三方App频繁启动Activity,导致AMS的Handler消息队列积压,整个系统都变得迟钝。后来我们加了一个限流策略,才把问题解决。
个人经验:AMS启动后,会调用setSystemProcess()把自己注册到ServiceManager中。这时候其他进程就能通过Binder拿到AMS的代理了。但注意,此时AMS还没完全准备好——它还需要等待systemReady()被调用,才能开始处理App的请求。
7.2 Activity栈管理:TaskRecord与ActivityRecord
Activity栈管理,说白了就是管理“哪个Activity在哪个任务里”。AMS用两个核心数据结构来干这事:ActivityRecord和TaskRecord。
- ActivityRecord:代表一个Activity实例。它记录了Activity的状态、Intent、所在进程等信息。
- TaskRecord:代表一个任务栈。它包含了一组ActivityRecord,这些Activity按启动顺序排列。
你想想看,当你在手机上点开一个App,再点开另一个App,再切回来——这些操作背后,AMS都在维护一个叫mStackSupervisor的对象。它管理着所有的TaskRecord和ActivityRecord。
// Activity栈的核心数据结构(简化)
class ActivityStackSupervisor {
// 所有任务栈
ArrayList<TaskRecord> mTaskHistory;
// 当前聚焦的栈
TaskRecord mFocusedStack;
// 根据Intent查找或创建Task
TaskRecord findTaskToMoveToFront(TaskRecord task, int flags,
ActivityRecord source) {
// 查找逻辑...
}
}
我记得有一次调试一个“App启动后闪退”的问题,查了半天发现是TaskRecord的mActivities列表里出现了空指针。原因是某个Activity被销毁后,AMS没有及时清理它的ActivityRecord引用。嗯,这种边界情况在源码里其实挺多的。
避坑指南:我曾经在适配Android 10时发现,launchMode="singleTask"的Activity在特定场景下会创建新的Task,而不是复用已有的。原因是AMS在startActivityUnchecked()中判断Task归属时,会检查taskAffinity属性。如果没设置,默认使用包名,这就可能导致“看起来应该复用,实际上新建了”的情况。
7.3 Activity生命周期管理:谁在背后调度?
Activity的onCreate()、onResume()、onPause()这些方法是谁调用的?很多人以为是App自己,其实不是。这些调用都来自AMS。
AMS通过ApplicationThread这个Binder接口,向App进程发送生命周期变更的请求。App进程收到请求后,再通过ActivityThread的H(Handler)把消息分发到主线程,最终调用Activity对应的方法。
// AMS端:通知App进入Resume状态
void resumeActivity(IBinder token, ...) {
// 找到对应的ActivityRecord
ActivityRecord r = ActivityRecord.forToken(token);
// 通过ApplicationThread通知App
r.app.thread.scheduleResumeActivity(r.appToken, ...);
}
// App端:收到通知后执行
private class H extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case RESUME_ACTIVITY:
// 最终调用Activity.onResume()
handleResumeActivity(...);
break;
}
}
}
这里有个关键点:AMS和App进程之间的通信是异步的。AMS发送一个schedulePauseActivity()后,不会傻等App处理完。它会继续处理其他请求。那怎么保证生命周期顺序呢?靠的是ActivityRecord里的状态机。
每个ActivityRecord都有一个state字段,取值如INITIALIZING、RESUMED、PAUSED、STOPPED等。AMS在切换状态前,会检查当前状态是否合法。比如,你不能从一个STOPPED的Activity直接跳到RESUMED——必须先经过STARTED。
核心逻辑:Activity的生命周期管理,本质上是AMS和App进程之间通过Binder进行状态同步的过程。AMS维护全局状态,App维护本地状态,两者通过ApplicationThread保持一致性。
7.4 进程优先级与OOM调整:谁该被杀死?
Android系统有个特点:它不会主动杀死进程。当内存不足时,它会根据优先级选择“最不重要”的进程杀掉。这个优先级就是由AMS来维护的。
AMS为每个进程维护一个ProcessRecord,里面有个maxAdj字段,代表进程的OOM调整值。这个值越小,进程越不容易被杀死。
| 进程类型 | adj值 | 典型场景 |
|---|---|---|
| 系统进程 | -1000 | SystemServer |
| 前台进程 | 0 | 当前正在交互的App |
| 可见进程 | 100 | Activity可见但不在前台 |
| 服务进程 | 200 | 正在运行Service的App |
| 后台进程 | 300 | Activity已暂停的App |
| 空进程 | 900 | 没有任何组件的进程 |
AMS会在updateOomAdjLocked()方法中,遍历所有进程,重新计算它们的adj值。计算依据是什么?主要是看进程里有哪些组件正在运行。
举个例子:如果一个进程里有一个前台Activity,那它的adj就是0。如果这个Activity被暂停了,但还有一个前台Service在运行,那adj会变成200。如果Service也停了,那就变成300。
我遇到过一个问题:某个App在后台播放音乐,但用户切到其他App后,音乐被卡顿了。查下来发现,AMS把那个进程的adj算成了300(后台进程),结果系统内存紧张时把它杀了。解决方案是让音乐播放使用前台Service,这样adj就变成了200,被杀的概率大大降低。
个人建议:如果你想让自己的App在后台更“抗杀”,可以考虑使用前台Service。但注意,前台Service会显示一个通知,用户是可以关掉的。另外,不要滥用这个机制——我曾经见过一个App,为了保活,开了5个前台Service,结果被用户骂惨了。
7.5 知识体系总览
下面这张图是我自己整理的AMS核心知识结构。你可以把它当作一个“地图”,以后遇到相关问题,先定位到图中的某个节点,再深入细节。
这张图把AMS的核心职责分成了四个部分:启动流程、栈管理、生命周期、进程优先级。每个部分之间都有联系——比如,启动一个Activity会涉及栈管理和生命周期,而进程优先级又会影响Activity能否正常运行。
好了,关于AMS的核心内容就讲到这里。说实话,AMS的代码量很大,但核心逻辑并不复杂。你只要抓住“状态机”和“Binder通信”这两条主线,就能看懂大部分代码。下次遇到App启动慢、闪退、被杀等问题,不妨先从AMS的角度去分析——说不定会有意想不到的收获。