AMS和app通信的小秘密

欢迎来到童话镇,让我带你揭开AMS(ActivityManagerService)和App之间那点"通信小秘密"。我们用一个有趣的比喻来贯穿整个讲解,保证你听得懂、记得住,体验系统设计的精妙之处!


引子:一场热闹的"快递大战"

想象一下,AMS是京西快递在"系统市"的总部调度中心,它权力巨大,负责调度整个城市的快递业务(管理所有App的Activity、Service等)。

而一个个App(比如淘宝、微信)就是分布在城市各处的分公司。这些分公司每天都要向总部发送无数请求:

  • 淘宝:"总部总部,我要开一个新的商品详情页!(startActivity)"
  • 微信:"总部总部,我要后台运行语音通话!(startForegroundService)"
  • 美团:"总部总部,我申请使用GPS权限!(requestPermission)"

如果所有分公司的快递员都一窝蜂地冲向总部调度中心,那会是什么场景?门口肯定堵得水泄不通,调度员会被吵得头昏脑胀,根本无法正常工作。

Android系统也面临着同样的问题:成百上千个App进程可能会同时向AMS发起请求,如何高效、有序、不出错地处理这些海量并发请求?

系统的解决方案堪称经典,它用了两大法宝:

  1. Binder线程池 :总部设立的多个接待窗口
  2. Handler消息队列 :调度中心CEO办公桌上的一个"待办文件筐"

下面,我们就跟着一份"淘宝"的快递,看看它是如何被处理的。


第一章:出发!快递员Binder的征程

淘宝App(客户端进程)想要启动一个新的Activity。它不会自己直接跑去找AMS,而是叫来一个Binder快递员

源码视角:

在App端,我们调用 startActivity(),最终会通过 ActivityTaskManager.getService().startActivity(...) 来完成。这个 getService() 拿到的就是AMS在客户端的Binder代理对象(Proxy) 。你可以把它理解为总公司给分公司配的一部专用电话

java 复制代码
// 在App进程中,这是一次Binder调用
// IActivityTaskManager是一个接口,它的实现是在系统进程的AMS那边
// getService()拿到的是AMS的Proxy代理对象
IActivityTaskManager.getService().startActivity(...);

当分公司(淘宝)拿起电话(调用代理对象的方法),Binder驱动(一个神通广大的底层系统)会神奇地把这个请求派送给总部调度中心(AMS进程)的一个空闲的Binder线程

这里就是第一层并发处理:Binder线程池。

AMS进程在启动时,会初始化一个Binder线程池(通常默认最大16个线程)。这就像是总部设立了16个接待窗口

cpp 复制代码
// 系统进程启动时,这样初始化Binder线程池
ProcessState::self()->startThreadPool();

现在,我们的"启动Activity"请求,被随机分配到了3号窗口 。3号窗口的Binder线程接收到这个请求包,它需要找AMS的真正对象(一个叫 ActivityTaskManagerService 的Java对象)来处理。

但如果这时有100个请求同时到来,16个窗口都满了,后续的请求就得在队列里等着。这已经避免了100个请求直接冲击AMS的核心业务逻辑。


第二章:混乱的接待室与有序的CEO

现在,3号窗口的Binder线程(我们叫他小哥Binder-3)拿着"淘宝"的请求包,跑进了AMS总部的"业务接待大厅"。

大厅里一片繁忙景象:

  • 窗口1的Binder线程正在处理"微信"的请求。
  • 窗口2的Binder线程正在处理"美团"的请求。
  • ...
  • 窗口16的Binder线程刚处理完"支付宝"的请求。

如果所有这些Binder线程都直接去调用AMS对象的方法,会怎样?

AMS对象内部的成员变量(比如记录当前Activity栈的数据)可能会被多个线程同时修改,导致数据错乱、状态不一致 !这就是可怕的并发问题

怎么解决?AMS的设计者想到了一个绝妙的主意:

"所有人都不准直接找我!把你们的请求都写成纸条,放到我办公室门口的那个'待办文件筐'(MessageQueue)里,我一个个处理!"

------ AMS CEO (ActivityManagerService)

这个"待办文件筐",就是 Handler机制中的消息队列(MessageQueue)

而负责往筐里扔纸条的,就是那些Binder线程。最终从筐里取纸条处理的,是AMS主线程(也叫主消息循环线程 )的 Handler

源码视角:

ActivityTaskManagerService 中,几乎所有重要的方法都不是直接处理业务逻辑。 instead,它们会先把请求打包成一个 Message,然后通过 Handler 发送出去。

我们以 startActivity 为例(路径已简化):

  1. Binder线程执行: ActivityTaskManagerService.startActivity()
  2. 委托给内部类: 调用 ActivityTaskManagerService.getActivityStartController().obtainStarter(...).execute()
  3. 切换到主线程: 这个 ActivityStarterexecute() 方法最终会调用 setRealCallingPid 等方法,但最关键的一步是:
java 复制代码
// 在ActivityStarter中
int execute() {
    try {
        // ... 一些参数检查 ...
        // 最关键的一步:调用到ActivityTaskManagerService的某个方法,该方法内部使用Handler
        return startActivityMayWait(...);
    } finally {
        // ...
    }
}

// 在ActivityTaskManagerService中
private int startActivityMayWait(...) {
    // ...
    // 使用主线程的Handler,发送一个EXECUTE_TRANSACTION的消息
    // 并将详细信息封装在ClientTransaction对象中
    mH.sendMessage(msg);
    // ...
}

看到 mH.sendMessage(msg) 了吗?这个 mH 就是AMS主线程的 Handler

现在,故事的高潮来了:

小哥Binder-3 在3号窗口接到淘宝的包裹后,一路小跑进大厅,但他没有直接去找AMS CEO,而是写了一张纸条(Message),上面写着"淘宝想启动Activity",然后把这张纸条和包裹一起,放进了CEO办公室门口那个唯一的、神奇的"待办文件筐" (MessageQueue)里。

放完之后,小哥Binder-3 的工作就完成了!他可以立刻返回3号窗口,去接待下一个客户了。他不需要等待CEO处理完这个请求。

与此同时,窗口1的Binder小哥 (处理微信请求)、窗口2的Binder小哥(处理美团请求)...他们也都做了同样的事情:写完纸条,扔进同一个文件筐,然后立刻返回。

至此,所有并发的请求(来自多个Binder线程、代表多个App),都被转换成了一个有序的、串行的队列(MessageQueue)


第三章:运筹帷幄的CEO------主线程Handler

现在,所有人的目光都聚焦在那个"待办文件筐"(MessageQueue)和CEO身上。

AMS进程的主线程,也就是我们的CEO ,它有一个核心工作:不停地绕着这个文件筐转(Looper循环)

java 复制代码
// 系统服务器主线程的Looper循环
public static void main(String[] args) {
    // ...
    Looper.prepareMainLooper(); // 准备主循环器
    // ... 初始化AMS等服务 ...
    Looper.loop(); // 开始无限循环,处理消息
}

每当CEO看到文件筐里有新的纸条(Message),他就会拿出来,看看纸条上写着什么指令,然后亲自处理对应的业务。

比如,他拿出第一张纸条,是窗口2的Binder小哥扔进来的"美团申请权限",CEO就处理权限逻辑。

处理完后,他拿出第二张纸条,是窗口1的Binder小哥扔进来的"微信启动服务",CEO就处理服务启动逻辑。

然后他拿出第三张纸条,是窗口3的Binder小哥扔进来的"淘宝启动Activity",CEO就检查Activity栈、分配任务、记录状态、通知其他App...

因为所有事情都是CEO一个人(主线程)串行处理的,所以根本不可能出现数据竞争的问题。他永远一次只做一件事,做得井井有条。

这就是第二层并发处理:将多线程的并发请求,通过Handler机制,切换到单线程(主线程)串行执行。


全景时序图:一次完整的旅程

下面我们用一张时序图,把淘宝发起一次 startActivity 的完整通信流程串起来:

总结与精妙之处

让我们回顾一下这个设计模式:

  1. Binder线程池(接待窗口)

    • 作用:解决"连接并发"问题。应对海量客户端的连接请求,避免它们阻塞。
    • 特点多线程,负责快速接收请求,本身不处理复杂业务。
  2. Handler消息机制(待办文件筐 + CEO)

    • 作用:解决"业务并发"问题。将复杂的、需要共享状态的业务逻辑串行化,避免线程安全问题。
    • 特点单线程,所有业务决策有序进行,保证系统状态的一致性。

这种"多线程接收 -> 串行化处理"的架构,是Android系统服务(如AMS、PMS等)应对并发请求的经典模式,也是系统稳定性的基石之一。

所以,下次当你点击App里的一个按钮跳转页面时,可以想象一下,背后有一个Binder快递小哥和一位运筹帷幄的AMS CEO,正通过这套精妙的协作机制,为你繁忙地工作着呢!希望这个故事能让你对Android系统的通信机制有更深刻的理解。

相关推荐
奥尔特星云大使8 小时前
MySQL 慢查询日志slow query log
android·数据库·mysql·adb·慢日志·slow query log
2501_9159184112 小时前
iOS 框架全解析,原生框架与跨平台框架对比、开发应用打包与 App Store 上架实战经验
android·ios·小程序·https·uni-app·iphone·webview
K24B;13 小时前
多模态大语言模型LISA++
android·人工智能·语言模型·分割·多模态大语言模型
molihuan18 小时前
开源 全平台 哔哩哔哩缓存视频合并 Github地址:https://github.com/molihuan/hlbmerge_flutter
android·flutter·缓存·ffmpeg·开源·github·音视频
奶糖 肥晨18 小时前
批量重命名技巧:使用PowerShell一键整理图片文件命名规范
android·服务器·数据库
Momentary_SixthSense19 小时前
如何对较长的Stream链进行Debug
android·java·开发语言
little_fat_sheep20 小时前
【Rive】rive-android源码分析
android
教程分享大师21 小时前
新魔百和m401h全部版本当贝桌面固件卡刷包和线刷包带adb权限
android
rufeii1 天前
网鼎杯 2020 青龙组
android
我要升天!1 天前
MySQL表的内连和外连
android·mysql·adb