恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Android系统核心服务AMS深度解析:架构、职责与进程管理
首页
资讯中心
/
Android系统核心服务AMS深度解析:架构、职责与进程管理
Android系统核心服务AMS深度解析:架构、职责与进程管理
发布时间:2026/10/20 7:31:09
做Android开发的人迟早会遇到一个绕不开的名字——AMS。ActivityManagerService很多三年五年的开发者一听到这几个字母就有点心虚因为它太底层、太庞杂网上资料要么是源码注释翻译要么是通篇Fragment/ActivityRecord对象图看两段就劝退了。我自己也是磕磕绊绊从只会调startActivity到能顺着一次点击把链路捋到SystemServer中间踩了不少坑。这系列文章就是想把AMS从定位、架构到核心流程讲清楚第一期先把骨架搭起来让你知道AMS是谁、管什么、长什么样、从哪儿来。内容尽量通俗但该有的源码细节我会给出指向方便你自己去验证。如果你还在应用层写业务这篇会帮你建立系统级的全局视角如果你已经开始啃framework这篇可以当一份导读地图省得在源码里乱撞。我尽量用“人和人打交道”的方式来讲AMS而不是堆砌术语。1. AMS到底是什么先说清楚它的职责边界1.1 一次点击背后谁在“安排”一切先来个最贴近日常的场景你手指点在桌面上某个App图标上系统启动这个应用界面就出来了。中间发生了什么看起来只是Launcher调了一个startActivity但真正干活的其实是系统里一个常驻的系统服务它就是ActivityManagerService。AMS不是一个普通的后台线程也不是某个App内部的管理器。它是运行在系统进程SystemServer里的一个核心服务相当于Android世界的“管家”。桌面App负责把页面画出来但“哪个App可以启动”“它的Activity长什么样”“什么时候该暂停、什么时候该恢复”“内存不够了先杀谁”这些都是AMS在决策。你可以把AMS理解成一个调度中心应用里的Activity只是演员而AMS是导演谁登场、谁下场、按什么顺序切换、舞台怎么分配都由它说了算。应用层感觉不到它的存在是因为系统通过Binder把这套能力封装成了透明的调用接口你只需要调用startActivity剩下的事有人替你安排。1.2 它在系统架构里的具体位置从系统架构看Android从上到下大致是应用层、Framework框架层、系统运行库层、Linux内核层。AMS属于Framework层里的SystemServer进程与它平级的还有WindowManagerService、PackageManagerService、ActivityTaskManagerServiceATMS后来从AMS中拆出来的等。这里要先提醒一句不同Android版本里AMS的代码结构变化很大。Android 10之后谷歌把Activity相关的任务栈管理拆到了ActivityTaskManagerServiceAMS本身则更聚焦在进程管理、权限校验、组件调度等事务上。很多老博客用Android 8甚至更早版本的源码讲AMS对象关系图都不一样你看的时候一定先确认源码分支版本否则对着不同结构会越看越乱。对于学习来说不必过度纠结“到底AMS负责哪几行代码”更重要的是理解它的职责分区。如果把框架层比作一家公司AMS就是行政总监WMS管窗口布局、PMS管包安装卸载、NotificationManagerService管通知各司其职而AMS管的是“Activity这整个组件的生命周期和任务栈编排”。1.3 AMS核心职责我总结下来就五件事第一件事是调度Activity生命周期启动、暂停、停止、销毁这些状态切换不是Activity自己拍板而是AMS发消息驱动应用进程只是配合执行。第二件事是管理和调度任务栈你要知道Android多任务视图里那些“卡片”是怎么来的其实就是AMS维护的TaskRecord在起作用它记录了一组Activity的组合关系栈怎么压入、怎么弹出都有规则。第三件事是进程管理AMS根据每个App当前的状态给进程评分内存吃紧时低内存回收机制会按这个评分选择性地杀进程这就是为什么你在后台挂着的应用过一阵子就“冷启动”了。第四件事是组件权限校验与Intent解析你启动了别的应用里的ActivityAMS要检查是否存在、目标组件是否允许外部访问、Intent能不能解析到对应的组件这些校验都在AMS这里完成。第五件事是系统消息中枢很多系统级广播、进程间状态同步的入口都在AMS比如开机后PackageManager扫描完所有应用要通知AMS“包列表准备好了”这中间就有复杂的流程调用。理解这五件事你就能明白为什么AMS是所有Android系统服务里最核心的一个。它像人体的中枢神经不受它支配的系统服务几乎不存在。2. 认识AMS的核心数据结构记住这几个关键角色2.1 ActivityRecord、TaskRecord、ActivityStack要深入AMS源码你早晚会碰到这几个类ActivityRecord、TaskRecord、ActivityStack、ActivityStackSupervisor。它们之间的关系我建议你用一套“实体-容器”的思维去记。ActivityRecord是Activity在系统侧的“档案袋”一个Activity实例在系统侧对应一个ActivityRecord里面记录了它的componentName、Intent、启动模式、当前状态、所在进程等元信息。它不等同于应用里的Activity对象更准确的比喻是“护照Activity是本人ActivityRecord是护照上的记录页”。TaskRecordAndroid 10后叫Task是任务栈的载体一个Task里包含一组ActivityRecord它们在界面上就是同一个“返回流”。比如你从首页进详情页再进支付页这三个页面通常在一个Task里按下返回键时ActivityRecord一个接一个从Task中弹出。ActivityStack是更高一层的容器容纳了一组Task。Android中有前台栈、后台栈、Home栈等不同堆栈用来组织处于不同状态的Task。在更早些的版本里ActivityStackSupervisor负责协调所有这些栈以及它们与AMS之间的交互。简单理解ActivityStack是“舞台”“Task”是节目单ActivityRecord是具体的演员。这里最容易踩的坑是把“Task”和“ActivityStack”搞混。你可以这样记Task是纵向的返回栈关系Stack是横向的前后台分组。同一个Stack可以包含多个Task前台只有一个Stack但屏幕上可能同时存在多个可见的Task形态比如分屏。2.2 ProcessRecordAMS眼里没有App只有进程对于AMS来说它很少直接操作一个“App”的概念而是操作ProcessRecord。ProcessRecord是某个应用进程在系统侧的代表里面保存了进程优先级、pid、uid、当前运行的Activity列表、ANR相关信息、已绑定的服务等。这里有个需要纠正的印象很多人以为Activity和进程是一一对应的其实不是。同一个应用的所有Activity、Service、BroadcastReceiver都可以跑在同一个进程里而不同应用如果在Manifest里配置了相同的sharedUserId或通过某种方式指定了同进程它们也可以共有同一个进程。AMS只关心进程级的状态不会真正去管“哪个类在跑”——这是应用进程内部的事情。在进程管理上AMS维护了几个关键列表比如mLruProcesses按最近使用情况排序的进程表这个列表直接影响进程被杀的顺序。进程更新前台状态、某个组件绑定、某个服务启动都会改变它在LRU列表中的位置。顺带一提很多内存优化方案里说的“进程被杀死后重新拉起”本质是AMS根据最近任务、恢复策略等手段重新创建进程并恢复Activity状态的过程而不是“原来的对象复活了”。搞清楚ProcessRecord你才能理解后续我们讲进程优先级时相关机制的本质。2.3 Handler、消息循环与线程模型AMS运行在SystemServer进程中它的主线程是一个消息循环线程所有对Activity的调度动作最终都会通过Handler发送到主线程的消息队列里执行。为什么要这么做因为AMS要面对来自无数App进程的并发Binder调用如果每个请求都直接操作内部数据结构不加锁、不排队状态早就乱套了。这里的核心机制是AMS接收到的Binder请求可能来自任何线程但真正执行逻辑的地方是系统主线程。它把请求包装成消息投递到主线程Handler的MessageQueue然后串行地逐条执行。我建议你记住这个模型因为很多ANR问题的排查思路追根溯源都会拉回到这条消息链路上某一条系统消息处理超时导致后续的调度请求全部排队最终表现为“应用无响应”。这个设计也解释了为什么AMS源码里充斥着大量类似mActivityTaskManagerInternal、mHandler.removeMessages(...)的调用——它需要精确地控制某段逻辑在什么时机执行、如何取消定时任务。如果你在看源码时看到某个方法直接调用了另一个进程的接口那通常是Binder IPC调用如果是通过内部Handler发送消息那多半是跨线程调度。把这两条线索理清了源码主干基本就顺了。3. AMS从哪儿来系统启动时发生了什么3.1 SystemServer的“演员表”AMS不是某次点击时被临时创建的它是系统开机过程中SystemServer进程初始化出来的常驻服务。要理解AMS的起源你需要先知道SystemServer的角色Android系统在启动时会先由init进程启动Zygote进程Zygote再fork出SystemServer进程。SystemServer是所有Java层系统服务的“宿主”服务列表几十上百个它们都在这里被依次创建并注册。这些服务的启动顺序不是随意的而是分阶段的先是基础的LED服务、电池服务、振动服务等然后是AMS、WMS、PMS这些重量级角色最后是一些非关键服务。为什么AMS这么靠后因为它需要前面某些服务已经就绪比如电源管理服务准备好之后AMS才能拿到唤醒锁去管理Activity显示用户管理服务准备好了AMS才能做用户相关的权限判断。在Android 10往后的代码里AMS的启动入口已经分散了ActivityTaskManagerService在startActivityTaskManagerService()方法里创建AMS则在后续的startActivityManagerService()里创建。你可以从SystemServer的run()方法看起一路追踪下去就能看到完整的初始化瀑布流。3.2 AMS的构造过程拆开看就三步AMS构造函数里做的一件事是创建自己的内部Handler和线程为后续的消息循环做准备。然后是创建ActivityTaskManagerService的实例把任务栈相关的逻辑单独交给它。最后是各种内部状态的初始化比如LRU进程列表、最近任务列表等。之后SystemServer会调用AMS的startSystemServerProcess()之类的流程让AMS知道自己现在运行在system_server进程里并设置必要的权限上下文。接下来是setSystemProcess()这个方法会把AMS注册到全局ServiceManager并写入系统进程对应的ProcessRecord。这一步完成之后其他进程才能通过ServiceManager查询方式拿到AMS的Binder代理。还需要将一些系统包如framework-res.apk预置到AMS的包管理状态中这样后续的组件解析才有东西可查。最后调用installSystemProviders()安装一些系统Provider比如SettingsProvider这样后面的系统组件的默认参数才有存取的地方。我建议大家不要死记这些方法名而是理解这条链创建实例→注册到上下文→写入进程信息→安装系统Provider→系统就绪。这样你单独去看某一版本的源码时不至于被几十个方法名的顺序绕晕。3.3 开机完成后AMS等待“开工”到这里AMS并没有真正“开始工作”它只是把“办公桌”摆好了。开机过程还在继续当PackageManager扫描完磁盘上所有APK把应用包信息收集完毕后系统会通过回调把“包列表准备好了”这个信号传给AMS。AMS拿到信号后开始接受并处理来自应用的绑定请求、startActivity请求等。这里有个细节在AMS完全就绪前如果某个进程提前发来了请求会发生什么实际上Zygote和SystemServer启动完成后系统进程自己会先启动一个Home应用通常在Launcher启动完成后其他App进程还不会被真正唤醒。AMS要确保系统进入稳定可交互状态后才开始分发应用层的调度指令。从“写代码的人”的角度理解AMS启动过程最大的价值在于它让你知道AMS不是一个抽象概念而是一个有完整初始化顺序、有明确依赖关系的Java对象。以后你看到线上崩溃日志里出现ActivityManagerService.failure之类的字样就不会一头雾水至少你能判断它是处在初始化阶段还是运行阶段。4. 一次startActivity的完整旅程把链路走一遍4.1 从应用侧发起到AMS收到请求Activity启动的入口在应用进程侧最常见的是ContextImpl.startActivity()它最终会走Instrumentation.execStartActivity()。这里Instrumentation像是一个“应用侧的特派员”负责把启动指令发给系统侧同时还能监控启动过程中的异常。在execStartActivity中调用ActivityTaskManager.getService().startActivity()这个getService()拿到的就是一个Binder代理实际对应的远端是ActivityTaskManagerServiceAndroid 10之后的实现。这个调用会通过Binder驱动跨进程传输到system_server进程在这里系统侧才拿到了真正的Intent、调用者包名、用户ID等参数。这个地方要额外理解的是跨进程调用不是“方法同步执行”而是“把请求打包发给远端进程等待返回”。Binder线程池中的某个线程拦截到请求解包后交到系统侧线程池或直接调用目标方法。所以你在源码里能看到很多onTransact相关的异常这些就是Binder传输层在处理请求时暴露出来的问题。从应用发起进程到AMS校验结束整条路径里有一句话要记住应用调用方的startActivity最终都会变成系统侧ActivityTaskManagerService的一次本地调用而系统侧返回的RemoteException会被Instrumentation捕获并转成应用层异常。4.2 AMS侧的关键处理校验、解析与任务栈决策ActivityTaskManagerService收到请求后会经过很长一段处理链。首先是权限校验调用方有没有权限启动目标Activity目标Activity是否允许被外部启动这里涉及exported属性、权限声明等。如果不满足直接抛SecurityException。接着是Intent解析系统要解析出Intent到底对应哪个组件。如果是显式Intent直接拿到componentName如果是隐式Intent系统要遍历已安装的包匹配IntentFilter。匹配结果可能有好几个用户没指定默认选择时系统会弹出ResolverActivity让用户选择这也解释了为什么你点击一个链接时偶尔会弹出“选择打开方式”的对话框。之后进入核心的ActivityStarter环节。这里会决定任务栈怎么处理涉及启动模式standard、singleTop、singleTask、singleInstance、flags、调用方的taskAffinity等参数。比如标记了NEW_TASK的会把Activity压入新任务栈singleTop的栈顶复用要判断当前栈顶Activity是否是同一个组件。任务栈决策是AMS体系里最“玄学”的部分之一因为多任务切换、返回栈顺序、分屏模式都会干扰最终结果。我的经验是遇到栈异常时优先确认Intent的flag和启动模式以及目标Activity的taskAffinity这三个参数的组合基本决定了Activity会去哪。4.3 进程不存在时AMS如何“从无到有”拉起一个进程如果目标Activity所属的进程还没启动AMS需要先创建进程。这个动作不能由AMS直接fork而是AMS通过Socket向Zygote发送请求让Zygotefork出一个新进程并在新进程中执行ActivityThread.main()。这里的逻辑很有意思AMS不会重复创建已经存在的进程。它会先检查目标进程是否已存在通过ProcessRecord查询如果存在直接走“进程已就绪”的流程只有不存在时才会与Zygote通信。进程创建完成后应用进程里会执行ActivityThread初始化并注册一个ApplicationThread对象到AMS那边这样AMS就拿到了和应用进程双向通信的通道。再往后AMS通过ApplicationThread通知ActivityThread创建和启动目标ActivityActivityThread会通过ClassLoader加载Activity类、创建实例、调用attach、走生命周期回调。你在onCreate里写的代码都是在ActivityThread上执行的而AMS始终在远端盯着状态的变化。整个链路串起来一次startActivity实际上经历了好几次跨进程通信应用进程→系统进程→Zygote→新进程→应用进程。这也是为什么冷启动比热启动慢很多——多了一次完整的进程fork和初始化。5. AMS的进程大权怎么管理、怎么杀5.1 进程优先级是怎么评出来的Android中每个正在运行的进程都有优先级但这个优先级不是Linux内核的传统优先级而是AMS根据进程当前的状态和应用行为综合评定的一个“adj”值。adj约大进程越容易被回收adj约小进程越重要越不会被杀。比如前台Activity所在进程的adj最小优先级最高可见但非前台的Activity所在进程其次有前台服务的进程排第三有后台Service的进程再靠后空进程优先级最低。这套规则分布在AMS的computeOomAdjLocked()里它会遍历所有进程反复评估最终算出每个进程对应的adj值和对应的内存回收级别。你手机上可能上百个进程同时存在但内存是有限资源Android系统的设计哲学就是“尽量让当前正在使用的App运行流畅牺牲后台进程”。所以你会发现平时后台进程被杀掉是常态不是系统“坏了”。理解adj机制对你的App开发有实际意义如果你的App要做后台长任务就必须想办法提高进程优先级比如利用前台Service或者配合通知让系统认为这个进程有用户可感知的内容而不是在后台偷偷干活。5.2 LRU列表与进程回收的双通道AMS维护了一个LRU列表根据进程最近被使用的时间排序最近使用的进程排前面其他靠后。这个列表的作用是配合adj共同决定回收顺序。即使两个进程adj相同LRU靠后的会先被回收。这里有个容易误解的点很多人以为“杀掉进程”是AMS自己动手杀其实不是。AMS只负责计算每个进程应该处于什么样的状态并更新内核驱动的参数通过写/proc/.../oom_score_adj真正执行杀动作的是内核里的lowmemorykiller低内存回收机制或AMS发起的Process.killProcess()。当系统内存低于某个阈值时lowmemorykiller会根据每个进程的oom_score_adj选择性地杀进程。所以进程被杀了你看到日志往往是一片低内存统计而不是一句“我决定杀了你”。你的App如果死得莫名其妙第一步要看一下Logcat里有没有lowmemorykiller相关日志大概率就是内存紧张时被系统选中了。想避免这个问题最有效的手段就是降低进程对内存的占用而不是祈祷系统“开恩”。5.3 进程恢复与状态保存AMS的“记忆能力”AMS除了会杀进程还要负责在进程被重新创建时帮你恢复现场。你肯定经历过一个App在后台被杀了重新打开时页面还在原来的位置甚至滚动位置都还在。这看起来神奇实际上是AMS在杀进程之前保存了Activity状态快照重新创建进程后恢复。这套机制的载体是ActivityRecord.savedState如果你重写了Activity的onSaveInstanceState系统可以在进程被杀前调用它保存数据如果没有重写系统只能恢复Activity的类名和Intent视图细节就丢了。这解释了为什么很多开发者遇到“进程被杀后数据丢失”问题不重写onSaveInstanceState进程被系统回收后重新创建时只能拿到Intent而Intent里没有的数据自然就丢了。所以遇到这类问题第一件事就是检查你是否正确处理了实例状态保存和恢复。AMS只提供“恢复的能力”要不要恢复完整取决于应用自己。6. 调试AMS的实用姿势直接看系统怎么说6.1 用dumpsys activity读取AMS的心跳如果你和我一样喜欢动手验证最常见的工具就是adb shell dumpsys activity。这条命令会把AMS当前的状态全部打印出来包括栈信息、任务信息、ActivityRecord列表、进程状态、LRU列表、内存信息、ANR记录等等。比如你想看当前前台Activity是谁可以执行adb shell dumpsys activity top它会打印栈顶的Activity详情包括它的包名、组件名、所在的TaskRecord ID、状态等。还有一个高频操作是查看进程的adj情况adb shell dumpsys activity processes它会列出所有进程的pid、uid、adj、进程状态等关键信息。调试时可以先把输出重定向到文件再慢慢看否则终端刷屏太严重。用adb shell dumpsys activity dumpsys_activity.txt保存到本地然后用编辑器搜索关键字效率会高很多。我强烈建议做一次立刻去查一下你手机上当前各个进程的adj值你会瞬间理解“前台进程、可见进程、服务进程”这些概念的实际排序。6.2 追踪Activity生命周期用ams日志或Debug工具系统日志里有很多和AMS相关的输出但不是默认全打出来的。你可以在设置里打开“调试”相关选项或者通过adb shell setprop log.tag.ActivityTaskManager DEBUG让ATMS输出更详细的日志。对于AMS本身也可以针对性设置log.tag.ActivityManager。另外Android Studio自带的“Layout Inspector”和“Profiler”只适合应用层调试真正要看AMS视角的状态还是依赖dumpsys和systrace。如果你在做启动优化或卡顿优化建议学一下systrace它能把系统各组件包括AMS的关键动作投递到时间线上一眼看出启动过程中哪一步耗时最长。调试经验说一条当Activity无法启动或莫名其妙跳回上一个页面时先别急着看业务代码。先执行adb shell dumpsys activity top确认栈顶是什么再执行adb shell dumpsys activity intents看看最近的intent记录。很多时候不是业务逻辑错了而是栈里的Activity状态不符合预期。6.3 实战案例一次Activity启动失败的排查路径我做性能优化时遇到过一个问题某App时不时出现点击按钮后界面闪一下就回桌面没有任何崩溃日志。查看logcat时发现一条明显异常Permission Denial: starting Intent ... requires ...。定位到问题后发现是目标Activity的exported设置为false而外部调起方不属于同一应用最终在AMS校验阶段被拦截了。另外一个常踩的坑是android:taskAffinity设置不当导致Activity被放到了错误的Task里。表现是打开一个页面后返回键没办法回到上一个页面而是跳回了桌面。用dumpsys查看后发现这个Activity的TaskRecord与期望的不在同一个Stack原因是设置了与其他页面不同的taskAffinity系统为它新建了一个Task。这类问题在业务代码里排查很久不出来的几率很高因为代码逻辑明明是对的。从AMS视角去查一眼就能定位。所以我的建议是任何“启动异常、返回异常、栈混乱”的问题都要第一时间怀疑启动模式/任务栈配置而不是怀疑生命周期回调没写对。7. 第二期预告接下来拆Activity启动核心链路的源码第一期的内容偏“地图式”的框架梳理把AMS的定位、数据结构、启动过程、Activity启动链路、进程管理和调试手段都过了一遍。你可以拿这份脑图去看源码方向就不会偏。不过真正把AMS吃透还得落到源码细节上尤其是ActivityStarter那段任务栈决策逻辑和Zygote进程启动的完整流程。下一期我打算拆一条更细的线从一次startActivity调用进入到ActivityStarter内部逐步分析任务栈的入栈、复用、单例模式处理逻辑并额外聊聊“为什么很多系统启动慢其实卡在AMS的消息队列上”。如果你手头正想做冷启动优化可以先把这一期里的进程创建链路和adj知识消化掉下一期讲线程调度时你会更有感觉。我自己最初学AMS的时候犯过一个错误总想一次性把所有方法背下来结果越看越迷糊。后来换成“按场景去阅读源码”的方式——比如就盯着“启动一个Activity要经历哪些校验”“杀掉一个后台进程的完整路径”这两个问题去读代码反而很快就把主干理顺了。你也别慌这套体系本身庞大能坚持挖下去收获绝对值得。