文章内容
- 一、从 Activity 里那一行调用说起:Context 只是代理
- 1.1 mBase 与 ContextImpl
- 1.2 startServiceCommon 交给 AMS 的五个参数
- 1.3 跨进程:一跳为什么是 ActivityManagerNative.getDefault()
- 二、ServiceRecord 从哪来:Intent 到 PMS 的解析与缓存
- 2.1 AMS.startService 的三道检查
- 2.2 userId 到 ServiceMap:按名字查、再按 Intent 查
- 2.3 只有查不到才 new ServiceRecord,并回写两张表
- 2.4 权限与导出:两种 ServiceLookupResult
- 三、把 onCreate 和 onStartCommand 投递到目标进程
- 3.1 进程已在跑:直接 realStartServiceLocked
- 3.2 进程没起来:startProcessLocked 与 mPendingServices
- 3.3 realStartServiceLocked 与 scheduleCreateService
- 3.4 handleCreateService:反射、ContextImpl、attach、onCreate
- 3.5 sendServiceArgsLocked 与 handleServiceArgs
- 四、ANR 是启动链上那颗没拆掉的延迟消息
- 4.1 埋下监控:bumpServiceExecutingLocked 与 scheduleServiceTimeoutLocked
- 4.2 20 秒与 200 秒:execServicesFg 决定时限
- 4.3 完成回报:serviceDoneExecuting 移除同一个消息
- 4.4 没有及时完成:serviceTimeout 到 appNotResponding
- 4.5 onStartCommand 返回值如何影响 stopIfKilled
- 五、bindService
- 5.1 不能跨进程的 ServiceConnection
- 5.2 getServiceDispatcher 与 ServiceDispatcher/InnerConnection
- 5.3 bindServiceLocked 登记了什么
- 5.4 复用同一条启动链
- 5.5 requestServiceBindingLocked 到 publishService
- 5.6 回调回到调用方主线程:RunConnection 与 doConnected
- 5.7 asInterface 代理
- 六、进程没起来时的一次回环
- 6.1 mPendingServices 与 attachApplicationLocked
- 七、stopService 与 unbindService
- 7.1 stopService:目标进程走到 onDestroy
- 7.2 unbindService:isServiceNeeded 决定是否真正停止
- 7.3 线程、静态变量与 GC root
- 八、小结
- 8.1 dumpsys、logcat 与 am 命令
startService() 和 bindService() 表面上对应两套 API,进入 Framework 后却共享了大半条启动链。两者都从调用方进程进入 ContextImpl,经 Binder 到达 system_server 中的 ActivityManagerService(下文简称 AMS),由 ActiveServices 查找或创建 ServiceRecord,再把生命周期调用投递给目标进程的 ActivityThread。
真正的分岔发生在 AMS 如何记录请求,以及目标进程收到哪类事务:
启动请求维护 pendingStarts,最终触发 onStartCommand();
绑定请求还要登记 ConnectionRecord,最终触发 onBind(),并把服务端 Binder 沿回调链送回调用方。
Service ANR 也不取决于 API 名称。ActiveServices 在生命周期调用发出前登记执行状态并投递延迟消息,只有相应的 serviceDoneExecuting() 及时返回,延迟消息才会被移除。
为避免把“调用 API”和“服务对象已经创建”混为一谈,先记住四个参与者:
| 所在位置 | 关键对象 | 职责 |
|---|---|---|
| 调用方进程 | MainActivity、ContextImpl、LoadedApk.ServiceDispatcher |
发起请求,包装 ServiceConnection,接收绑定回调 |
system_server |
AMS、ActiveServices、ServiceMap、ServiceRecord、ConnectionRecord |
校验、解析、缓存、调度进程与生命周期、监控超时 |
| 目标 Service 进程 | ActivityThread、Service 实例 |
创建 Service,调用 onCreate()、onStartCommand()、onBind()、onDestroy() |
| PMS 接口 | resolveService() |
把 Intent 解析成 ServiceInfo;调用发生在 system_server 内的服务管理流程中 |
一、从 Activity 里那一行调用说起:Context 只是代理
1.1 mBase 与 ContextImpl
Activity 继承自 ContextThemeWrapper,对外暴露的是 Context API;承担具体工作的是作为基础 Context 的 ContextImpl。因此,应用代码里的 startService(intent) 不会在 Activity 内直接创建 MessageService。它只是进入 ContextImpl,由后者整理调用方身份、Intent 类型和 userId,再请求 AMS。
下面的代码只保留与调用链有关的部分。它占住调用方主线程时也可能触发 ANR,但触发源不等同于 Service 执行超时。
// 代码位置 MainActivity.java 第 1—25 行// 声明演示页面类public class MainActivity extends Activity {// 保存后续停止服务所需的 Intent private Intent intent;// 定义接收绑定结果的回调 private final ServiceConnection serviceConn = new ServiceConnection() {// 处理服务连接成功 @Override public void onServiceConnected(ComponentName name, IBinder service) {// 把 Binder 转成业务接口 UserAidl api = UserAidl.Stub.asInterface(service);// 结束连接成功回调 }// 处理服务进程断开 @Override public void onServiceDisconnected(ComponentName name) {// 结束断开回调 }// 结束匿名对象 };// 进入页面初始化 @Override protected void onCreate(Bundle savedInstanceState) {// 调用父类初始化 super.onCreate(savedInstanceState);// 创建指向 MessageService 的显式 Intent intent = new Intent(this, MessageService.class);// 发起启动请求 startService(intent);// 发起自动创建式绑定 bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);// 用长时间阻塞复现主线程超时 Thread.sleep(30500);// 结束初始化 }// 响应停止按钮 public void testClick(View view) {// 请求停止启动态 Service stopService(intent);// 结束点击方法 }// 结束页面类}Thread.sleep(1000) 时,启动场景的日志顺序为 onCreate → onStartCommand,不会调用 onBind;绑定场景调用 onCreate 与 onBind。这组差异对应 Framework 投递的消息类型,不能用“都是启动 Service”一笔带过。

图 1 startService 与 bindService 共享三段跨进程主链,并在参数记录和生命周期投递处发生分岔。
1.2 startServiceCommon 交给 AMS 的五个参数
ContextImpl.startService() 很薄,主体位于 startServiceCommon()。它先检查 Intent,再调用 prepareToLeaveProcess(),随后通过 ActivityManagerNative.getDefault() 进入 AMS。传入的核心信息包括调用方 IApplicationThread、Service Intent、解析后的 MIME type、调用包名和 userId。
// 代码位置 core/java/android/app/ContextImpl.java 第 1236—1242 行// 定义 startService 的公共实现private ComponentName startServiceCommon(Intent service, UserHandle user) {// 捕获跨进程异常 try {// 校验 Service Intent validateServiceIntent(service);// 把 Intent 整理成可离开当前进程的状态 service.prepareToLeaveProcess();// 获取 AMS 代理并发起启动请求 ComponentName cn = ActivityManagerNative.getDefault().startService(// 传递应用线程、Intent 和解析类型 mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(// 传递包名与用户标识 getContentResolver()), getOpPackageName(), user.getIdentifier());AMS 可能用特殊 ComponentName 传回失败原因。包名为 "!" 表示权限不足,"!!" 表示无法启动;ContextImpl 把它们还原成 SecurityException。这也解释了返回类型为何是 ComponentName:正常时它标识实际组件,异常时还能承载受控的错误协议。
1.3 跨进程:一跳为什么是 ActivityManagerNative.getDefault()
在 Android 6.0 中,ActivityManagerNative.getDefault() 返回 AMS 的 Binder 接口代理。调用方看到的是 Java 方法,参数经过 Binder 驱动送到 system_server,再进入 AMS 的同名入口。入口拒绝 isolated caller 和携带文件描述符的 Intent,并在 AMS 锁内把请求交给 ActiveServices。
// 代码位置 services/core/java/com/android/server/am/ActivityManagerService.java 第 15661—15668 行// 定义 AMS 的 Binder 服务端入口public ComponentName startService(IApplicationThread caller, Intent service,// 接收解析类型、调用包和用户标识 String resolvedType, String callingPackage, int userId)// 声明大事务异常 throws TransactionTooLargeException {// 禁止隔离进程调用 enforceNotIsolatedCaller("startService");// 拒绝携带文件描述符的 Intent if (service != null && service.hasFileDescriptors() == true) {// 抛出非法参数异常 throw new IllegalArgumentException("File descriptors passed in Intent");// 结束文件描述符检查 }// 代码位置 services/core/java/com/android/server/am/ActivityManagerService.java 第 15676—15684 行// 在 AMS 全局锁内修改服务状态synchronized(this) {// 取得 Binder 调用方进程号 final int callingPid = Binder.getCallingPid();// 取得 Binder 调用方用户号 final int callingUid = Binder.getCallingUid();// 清除调用身份以执行系统侧工作 final long origId = Binder.clearCallingIdentity();// 把启动请求交给 ActiveServices ComponentName res = mServices.startServiceLocked(caller, service,// 传入类型、身份、包名和用户 resolvedType, callingPid, callingUid, callingPackage, userId);// 恢复原 Binder 身份 Binder.restoreCallingIdentity(origId);// 返回组件或错误哨兵 return res;// 退出 AMS 锁}这里的“跨进程”只是调用路径的一段。PMS 的解析、目标进程的创建以及回调到应用,都有各自的身份和调度边界。分析 Service 问题时,应同时记录当前方法属于哪个进程、持有什么锁、下一跳是本地调用还是 Binder 调用。
二、ServiceRecord 从哪来:Intent 到 PMS 的解析与缓存
2.1 AMS.startService 的三道检查
AMS 入口的检查可以按职责分成三层:
- 调用者不能是 isolated process;
- Intent 不能夹带文件描述符;
callingPackage不能为空。
进入ActiveServices.startServiceLocked()后,还会取得调用方进程记录、判断调用方是否前台,并调用retrieveServiceLocked()。因此ServiceRecord不是应用传入的对象,也不是 Service 实例,它是system_server中保存组件解析结果和运行状态的账本。
2.2 userId 到 ServiceMap:按名字查、再按 Intent 查
retrieveServiceLocked() 先通过 handleIncomingUser() 规范 userId,然后取得对应用户的 ServiceMap。
显式 Intent 可按 ComponentName 查询 mServicesByName;若未命中,再用 Intent.FilterComparison 查 mServicesByIntent。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1032—1043 行// 规范跨用户调用的 userIduserId = mAm.handleIncomingUser(callingPid, callingUid, userId,// 传入用户处理策略 false, ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);// 取得该用户的 ServiceMapServiceMap smap = getServiceMap(userId);// 读取显式组件名final ComponentName comp = service.getComponent();// 判断 Intent 是否带组件名if (comp != null) {// 优先按组件名查找 r = smap.mServicesByName.get(comp);// 结束显式查询}// 名字查询未命中时继续if (r == null) {// 构造用于过滤比较的 Intent 包装 Intent.FilterComparison filter = new Intent.FilterComparison(service);// 再按 Intent 过滤条件查找 r = smap.mServicesByIntent.get(filter);// 结束 Intent 查询}两张表指向同一个 ServiceRecord,索引目的不同。mServicesByName 适合组件身份明确的访问;mServicesByIntent 让同一过滤语义的 Intent 复用记录。这里所谓“缓存”是 system_server 内的运行态索引,不能等同于磁盘缓存,也不能推导出 Service Java 对象会永久存在。

图 2 retrieveServiceLocked 先查两张运行态索引,均未命中时才请求 PMS 解析并创建 ServiceRecord。
2.3 只有查不到才 new ServiceRecord,并回写两张表
两次查询都失败后,代码调用 PackageManager 的 resolveService(),得到 ResolveInfo 和 ServiceInfo。若组件确实存在,方法再次按解析出的规范组件名查表;只有仍未命中且 createIfNeeded 为真,才创建记录并写回两张索引。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1044—1055 行// 两张表都未命中时进入解析if (r == null) {// 捕获包管理服务的远程异常 try {// 声明解析结果 ResolveInfo rInfo =// 请求 PMS 解析 Service AppGlobals.getPackageManager().resolveService(// 传入 Intent、类型、标志和用户 service, resolvedType,// 使用 AMS 的标准包管理查询标志 ActivityManagerService.STOCK_PM_FLAGS, userId);// 提取 ServiceInfo ServiceInfo sInfo =// 对空解析结果做保护 rInfo != null ? rInfo.serviceInfo : null;// 判断组件是否存在 if (sInfo == null) {// 记录找不到 Service Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +// 补全日志文本 ": not found");// 返回未找到 return null;// 结束空结果分支 }// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1069—1084 行// 用解析后的规范组件名再次查表r = smap.mServicesByName.get(name);// 记录不存在且允许创建时建账if (r == null && createIfNeeded) {// 声明 Intent 比较键 Intent.FilterComparison filter// 克隆过滤相关字段,避免外部修改 = new Intent.FilterComparison(service.cloneFilter());// 创建重启辅助对象 ServiceRestarter res = new ServiceRestarter();// 初始化电量统计句柄 BatteryStatsImpl.Uid.Pkg.Serv ss = null;// 取得活动统计对象 BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();// 在统计对象锁内取 Service 统计 synchronized (stats) {// 查询该组件的服务统计 ss = stats.getServiceStatsLocked(// 传入 uid、包名和类名 sInfo.applicationInfo.uid, sInfo.packageName,// 传入 Service 名称 sInfo.name);// 退出统计锁 }// 创建 system_server 侧的 ServiceRecord r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);// 让重启器关联该记录 res.setService(r);// 写入组件名索引 smap.mServicesByName.put(name, r);// 写入 Intent 索引 smap.mServicesByIntent.put(filter, r);第二次启动同一 Service 时,如果记录仍在 ServiceMap,查询会直接命中,PMS 解析和 new ServiceRecord 都不会重复执行。但后续仍可增加新的 StartItem,onStartCommand() 也可能再次收到调用。ServiceRecord 复用不代表生命周期回调只发生一次。
这里有两个容易混起来的对象。ServiceInfo 由 PackageManager 解析 AndroidManifest.xml 得到,描述组件的静态声明:包名、类名、exported、permission 这些字段随 APK 安装而定,不随进程运行状态变化。ServiceRecord 是 system_server 在首次解析成功后创建的运行态记录,ServiceRecord.java 第 71 行把它持有的字段声明为 final ServiceInfo serviceInfo,也就是引用一份 ServiceInfo,同时还带 app、startRequested、pendingStarts、connections、executeNesting 这些只在 AMS 侧才有意义的字段。resolveService() 每次都可能返回新的 ServiceInfo 对象,但只要 mServicesByName 或 mServicesByIntent 命中,复用的就是同一个 ServiceRecord。把 ServiceRecord 理解成“更详细的 ServiceInfo”会错,它俩是静态声明与运行态记录的关系。
2.4 权限与导出:两种 ServiceLookupResult
解析完成后还要检查调用者是否有权访问组件。未导出的 Service 会返回带原因文本、但 record 为空的 ServiceLookupResult;声明了权限而调用者缺少权限时,也返回失败结果,并记录所需权限。上层据此区分“组件不存在”和“组件存在但不可访问”,最终形成不同的异常或返回值。retrieveServiceLocked() 末尾这段判断就是把两种失败分开的位置。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1099—1115 行// 解析成功后继续检查访问权限if (r != null) {// 调用 AMS 的组件权限检查 if (mAm.checkComponentPermission(r.permission,// 传入调用方 pid、uid,以及组件 uid 与是否导出 callingPid, callingUid, r.appInfo.uid, r.exported)// 权限检查未通过时进入失败分支 != PackageManager.PERMISSION_GRANTED) {// 先判断组件是否未导出 if (!r.exported) {// 记录未导出访问被拒绝 Slog.w(TAG, "Permission Denial: Accessing service " + r.name// 附加调用方 pid + " from pid=" + callingPid// 附加调用方 uid + ", uid=" + callingUid// 附加目标组件 uid + " that is not exported from uid " + r.appInfo.uid);// 返回 record 为空、reason 为未导出说明的结果 return new ServiceLookupResult(null, "not exported from uid "// 拼接 reason 文本 + r.appInfo.uid);// 结束未导出分支 }// 记录已导出但权限不足 Slog.w(TAG, "Permission Denial: Accessing service " + r.name// 附加调用方 pid + " from pid=" + callingPid// 附加调用方 uid + ", uid=" + callingUid// 附加所需权限名 + " requires " + r.permission);// 返回 record 为空、reason 为所需权限名的结果 return new ServiceLookupResult(null, r.permission);// 结束权限检查失败分支 }安全判断发生在 system_server,不能靠应用进程里的显式 Intent 绕过。即使调用方知道完整类名,exported 与组件权限仍然决定跨应用访问能否继续。
三、把 onCreate 和 onStartCommand 投递到目标进程
3.1 进程已在跑:直接 realStartServiceLocked
bringUpServiceLocked() 的分支先看目标进程是否已经有可用的 ApplicationThread。若 Service 本身已经关联到运行中的 app,方法可以直接发送新的启动参数;若只有进程存在,则取得 ProcessRecord 后调用 realStartServiceLocked()。这两种“已在跑”含义不同:前者可能不需要重建 Service,后者仍需投递创建事务。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1415—1427 行// 判断 Service 是否要求隔离进程final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;// 读取目标进程名final String procName = r.processName;// 声明目标进程记录ProcessRecord app;// 普通进程走现有进程查询if (!isolated) {// 按进程名和 uid 查 ProcessRecord app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);// 判断进程及 Binder 线程接口是否可用 if (app != null && app.thread != null) {// 捕获投递异常 try {// 把包关联到进程统计 app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);// 进入真实启动阶段 realStartServiceLocked(r, app, execInFg);// 成功后结束 bring-up return null;3.2 进程没起来:startProcessLocked 与 mPendingServices
没有可用进程时,AMS 调用 startProcessLocked()。此时不能立即执行 Service 生命周期,因为 ActivityThread 尚未通过 Binder 附着到 AMS。ServiceRecord 会进入 mPendingServices,等待进程启动完成后的回环。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1449—1467 行// 没有目标进程时进入创建流程if (app == null) {// 请求 AMS 启动目标进程 if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,// 标记进程因 Service 而启动 "service", r.name, false, isolated, false)) == null) {// 组织进程启动失败信息 String msg = "Unable to launch app "// 拼接包名 + r.appInfo.packageName + "/"// 拼接 uid + r.appInfo.uid + " for service "// 拼接原始 Intent + r.intent.getIntent() + ": process is bad";// 记录失败 Slog.w(TAG, msg);// 清理 Service 状态 bringDownServiceLocked(r);// 返回失败原因 return msg;// 结束启动失败分支 }// 隔离 Service 单独保存进程记录 if (isolated) {// 关联隔离进程 r.isolatedProc = app;// 结束隔离分支 }// 结束无进程分支}// 避免重复加入等待队列if (!mPendingServices.contains(r)) {// 等待进程附着后继续 mPendingServices.add(r);// 结束等待队列判断}
图 3 bringUpServiceLocked 在目标进程缺席时挂起 ServiceRecord,并在进程附着后回到 realStartServiceLocked。
3.3 realStartServiceLocked 与 scheduleCreateService
进入 realStartServiceLocked() 后,ServiceRecord 与 ProcessRecord 建立关联,Service 被加入进程的 services 集合。紧接着调用 bumpServiceExecutingLocked(..., "create") 登记执行状态,再通过目标进程的 IApplicationThread 调用 scheduleCreateService()。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1500—1506 行// 让 ServiceRecord 关联目标进程r.app = app;// 记录重启和活动时间r.restartTime = r.lastActivity = SystemClock.uptimeMillis();// 把 Service 加入进程集合final boolean newService = app.services.add(r);// 标记 create 生命周期正在执行bumpServiceExecutingLocked(r, execInFg, "create");// 更新进程 LRUmAm.updateLruProcessLocked(app, false, null);// 重算 OOM 调整值mAm.updateOomAdjLocked();// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1520—1524 行// 确保 Service 包完成 dex 优化mAm.ensurePackageDexOpt(r.serviceInfo.packageName);// 把进程状态至少提升到 Service 级别app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);// 跨进程投递创建 Service 请求app.thread.scheduleCreateService(r, r.serviceInfo,// 附带兼容信息 mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),// 附带进程状态 app.repProcState);scheduleCreateService() 这个名字里的 schedule 很重要:AMS 发出 Binder 调用,不等于目标进程已完成 onCreate()。目标进程的 ApplicationThread 把数据转成消息,ActivityThread 主线程再处理它。ANR 监控覆盖的就是这段“已经投递、尚未完成”的时间窗。
3.4 handleCreateService:反射、ContextImpl、attach、onCreate
目标进程收到创建消息后,handleCreateService() 取得 LoadedApk 和类加载器,反射构造 Service。随后创建该组件使用的 ContextImpl,构造或取得 Application,调用 Service.attach() 注入运行环境,才进入开发者熟悉的 onCreate()。
// 代码位置 core/java/android/app/ActivityThread.java 第 2854—2860 行// 取得目标包的 LoadedApkLoadedApk packageInfo = getPackageInfoNoCheck(// 使用应用信息与兼容配置 data.info.applicationInfo, data.compatInfo);// 初始化 Service 引用Service service = null;// 捕获反射创建异常try {// 取得包的类加载器 java.lang.ClassLoader cl = packageInfo.getClassLoader();// 按 Service 类名反射实例化 service = (Service) cl.loadClass(data.info.name).newInstance();// 进入异常分支} catch (Exception e) {// 代码位置 core/java/android/app/ActivityThread.java 第 2871—2881 行// 创建组件使用的 ContextImplContextImpl context = ContextImpl.createAppContext(this, packageInfo);// 把外层 Context 指向 Servicecontext.setOuterContext(service);// 取得或创建 ApplicationApplication app = packageInfo.makeApplication(false, mInstrumentation);// 向 Service 注入上下文、线程、token 和 AMS 接口service.attach(context, this, data.info.name, data.token, app,// 取得 AMS Binder 接口 ActivityManagerNative.getDefault());// 调用 Service.onCreateservice.onCreate();// 按 token 保存 Service 实例mServices.put(data.token, service);// 捕获完成回报的远程异常try {// 告诉 AMS 本次匿名生命周期执行完成 ActivityManagerNative.getDefault().serviceDoneExecuting(// 传回 token 与执行类型 data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);这里能看到 Service Java 对象属于目标进程,ServiceRecord 属于 system_server。两者靠 token 对应。把 ServiceRecord 称为 Service 实例,会导致后续对停止、重启和进程死亡的理解全部错位。
3.5 sendServiceArgsLocked 与 handleServiceArgs
创建完成只是第一段。startServiceLocked() 已把一个 StartItem 加入 pendingStarts,realStartServiceLocked() 后续调用 sendServiceArgsLocked(),再用 scheduleServiceArgs() 把参数送到目标进程。ActivityThread.handleServiceArgs() 最终执行 onStartCommand(data.args, data.flags, data.startId),并把返回值随 SERVICE_DONE_EXECUTING_START 回报 AMS。
pendingStarts 和 3.2 节的 mPendingServices 只差一个字,层级完全不同。mPendingServices(ActiveServices.java 第 138 行)挂在 ActiveServices 上,装的是等待目标进程创建完成的 ServiceRecord,进程起来之后记录就会被移出。pendingStarts(ServiceRecord.java 第 173 行)挂在每个 ServiceRecord 自己身上,装的是尚未投递给目标进程的 ServiceRecord.StartItem(类定义在第 120 行),每次 startServiceLocked() 都会追加一个新的 StartItem,sendServiceArgsLocked() 再把队首的 StartItem 经 scheduleServiceArgs() 送到目标进程,转成一次 onStartCommand() 调用。同一个 ServiceRecord 在进程已经起来、不再出现在 mPendingServices 之后,仍然会不断有新的 StartItem 进出 pendingStarts。
// 代码位置 core/java/android/app/ActivityThread.java 第 3008—3024 行// 声明生命周期返回值int res;// 普通启动参数进入 onStartCommandif (!data.taskRemoved) {// 调用 Service 并保存返回值 res = s.onStartCommand(data.args, data.flags, data.startId);// 任务移除事件走另一条回调} else {// 调用 onTaskRemoved s.onTaskRemoved(data.args);// 使用任务移除完成标志 res = Service.START_TASK_REMOVED_COMPLETE;// 结束任务类型分支}// 等待排队工作完成QueuedWork.waitToFinish();// 捕获向 AMS 回报的远程异常try {// 回报 start 生命周期已经完成 ActivityManagerNative.getDefault().serviceDoneExecuting(// 携带 token、startId 与 onStartCommand 返回值 data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);// 捕获 Binder 异常} catch (RemoteException e) {// AOSP 此处不做额外处理 // nothing to do.// 结束远程异常分支}// 启用即时编译支持ensureJitEnabled();录屏登记的 Thread.sleep(1000) 实验出现 onCreate → onStartCommand,与这两段投递顺序一致。若同一个 Service 已经创建,后续 startService() 可以只增加新的启动参数并再次进入 onStartCommand(),无需再次调用 onCreate()。
四、ANR 是启动链上那颗没拆掉的延迟消息
4.1 埋下监控:bumpServiceExecutingLocked 与 scheduleServiceTimeoutLocked
在 scheduleCreateService()、scheduleServiceArgs() 和 scheduleBindService() 之前,ActiveServices 都会调用 bumpServiceExecutingLocked()。它增加 executeNesting,记录 executingStart,并把 Service 放进所属进程的 executingServices。当集合从空变成一个元素时,系统安排超时消息。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1142—1154 行// 读取当前运行时间long now = SystemClock.uptimeMillis();// 只在最外层执行开始时登记进程状态if (r.executeNesting == 0) {// 记录本次执行是否按前台标准计时 r.executeFg = fg;// 取得进程统计中的 Service 状态 ProcessStats.ServiceState stracker = r.getTracker();// 判断统计对象是否存在 if (stracker != null) {// 标记 Service 正在执行 stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);// 结束统计分支 }// 判断 Service 是否已经关联进程 if (r.app != null) {// 加入进程的执行中集合 r.app.executingServices.add(r);// 汇总进程内是否存在前台执行 r.app.execServicesFg |= fg;// 第一个执行项负责安排超时 if (r.app.executingServices.size() == 1) {// 投递 Service 超时消息 scheduleServiceTimeoutLocked(r.app);// 结束首次执行判断 }// 结束进程存在分支 }这颗消息属于进程级监控。一个进程可以同时关联多个 Service,AMS 通过 executingServices 和各记录的 executingStart 找到真正超时的对象。它并非给每次 Java 回调各创建一个互不相关的定时器。

图 4 生命周期投递前安排 SERVICE_TIMEOUT_MSG,完成回报清空执行集合后移除该消息。
4.2 20 秒与 200 秒:execServicesFg 决定时限
AOSP 6.0 定义前台 Service 执行超时为 20 秒,后台为其十倍,即 200 秒。这里的前台/后台来自调度时传入的 execInFg,汇总到 ProcessRecord.execServicesFg。它不等同于后来版本里调用 startForegroundService() 后显示通知的“前台服务”概念。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 87—90 行// 定义前台执行超时为二十秒static final int SERVICE_TIMEOUT = 20*1000;// 定义后台执行超时为前台的十倍static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 2536—2546 行// 定义 Service 超时消息调度void scheduleServiceTimeoutLocked(ProcessRecord proc) {// 没有执行中 Service 或线程未附着时退出 if (proc.executingServices.size() == 0 || proc.thread == null) {// 不安排无效消息 return;// 结束早退条件 }// 读取当前时间 long now = SystemClock.uptimeMillis();// 创建 Service 超时消息 Message msg = mAm.mHandler.obtainMessage(// 使用统一的消息编号 ActivityManagerService.SERVICE_TIMEOUT_MSG);// 把目标进程放入消息 msg.obj = proc;// 按进程执行属性选择触发时间 mAm.mHandler.sendMessageAtTime(msg,// 前台二十秒,后台二百秒 proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));// 结束调度方法}录屏登记的 MessageService.onCreate() 内 Thread.sleep(25000) 会触发 ANR,与 20 秒窗口相符。这个观察只说明当时路径按前台执行标准计时;若把同样的耗时放到未被该监控覆盖的线程或改走不同进程状态,结果不能直接类推。
4.3 完成回报:serviceDoneExecuting 移除同一个消息
目标进程完成 onCreate()、onStartCommand()、onBind() 或停止流程后,会通过不同分支回报 serviceDoneExecuting()。AMS 最终进入 ActiveServices.serviceDoneExecutingLocked(),减少嵌套计数。计数归零后,记录从 executingServices 移除;当进程内不再有执行中的 Service,Handler 删除以该 ProcessRecord 为 obj 的 SERVICE_TIMEOUT_MSG。
回报时携带的 type 有三个取值,定义在 ActivityThread.java 第 170、172、174 行:SERVICE_DONE_EXECUTING_ANON = 0、SERVICE_DONE_EXECUTING_START = 1、SERVICE_DONE_EXECUTING_STOP = 2。划分依据是 AMS 侧要不要按 type 做额外记账。START 对应 handleServiceArgs() 回报 onStartCommand() 的返回值,AMS 侧进入 switch (res) 更新 stopIfKilled 等 StartItem 状态;STOP 对应 handleStopService() 回报 onDestroy() 已完成,AMS 侧校验该记录是否在 mDestroyingServices 中、executeNesting 是否等于 1;ANON 用在 onCreate()、onBind() 的非 rebind 分支和 onRebind() 这些不需要额外记账的位置。三个 type 共享的动作是减 executeNesting 和执行集合清理,只有 START 与 STOP 会触发各自的额外状态更新。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1980—1990 行// 完成一层 Service 执行r.executeNesting--;// 嵌套计数归零时清理执行状态if (r.executeNesting <= 0) {// 确认记录仍关联进程 if (r.app != null) {// 清除前台执行汇总 r.app.execServicesFg = false;// 从执行中集合移除 Service r.app.executingServices.remove(r);// 进程内没有其他执行项时撤销超时消息 if (r.app.executingServices.size() == 0) {// 从 AMS Handler 删除与该进程关联的超时消息 mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);所谓“拆弹”应理解为状态机完成闭环:目标进程回报、嵌套计数下降、执行集合清空、延迟消息删除。任一环节没有按时发生,消息都可能到期。只盯着 Thread.sleep() 会漏掉 Binder 阻塞、锁等待和主线程消息拥堵等同样能延迟完成回报的因素。
4.4 没有及时完成:serviceTimeout 到 appNotResponding
SERVICE_TIMEOUT_MSG 的编号是 12。AMS 的 MainHandler 收到后调用 mServices.serviceTimeout((ProcessRecord) msg.obj)。
// 代码位置 services/core/java/com/android/server/am/ActivityManagerService.java 第 1317 行// 定义 Service 执行超时消息的编号static final int SERVICE_TIMEOUT_MSG = 12;// 代码位置 services/core/java/com/android/server/am/ActivityManagerService.java 第 1615—1623 行// 处理 Service 执行超时消息case SERVICE_TIMEOUT_MSG: {// 命中过一次 dex 优化时先顺延一轮 if (mDidDexOpt) {// 清除 dex 优化标记 mDidDexOpt = false;// 重新构造一条同类型消息 Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);// 沿用原消息携带的进程对象 nmsg.obj = msg.obj;// 按标准超时时长重新投递 mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);// 本轮先返回,不继续处理 return;// 结束顺延分支 }// 真正进入 ActiveServices 的超时判定 mServices.serviceTimeout((ProcessRecord)msg.obj);// 结束该 case} break;serviceTimeout() 计算 maxTime,遍历执行中 Service,以 executingStart 判断谁越过时限。确认目标进程仍在 LRU 列表后,它保存诊断文本,并形成 executing service <shortName> 的 ANR 原因,最终交给 AMS 的 appNotResponding() 流程抓取 traces 和展示 ANR。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 2496—2511 行// 读取当前运行时间final long now = SystemClock.uptimeMillis();// 依据进程执行属性计算最早允许时间final long maxTime = now -// 选择前台或后台超时值 (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);// 初始化超时 ServiceServiceRecord timeout = null;// 初始化下一次检查时间long nextTime = 0;// 逆序遍历执行中 Servicefor (int i=proc.executingServices.size()-1; i>=0; i--) {// 取得一条执行记录 ServiceRecord sr = proc.executingServices.valueAt(i);// 判断开始时间是否早于阈值 if (sr.executingStart < maxTime) {// 记下超时对象 timeout = sr;// 找到后退出遍历 break;// 结束超时判断 }// 更新尚未超时记录中的较晚时间 if (sr.executingStart > nextTime) {// 保存下一次计算依据 nextTime = sr.executingStart;// 结束时间更新 }// 结束遍历}// 只处理仍属于活动进程列表的超时if (timeout != null && mAm.mLruProcesses.contains(proc)) {appNotResponding() 的入口接收进程、关联 Activity、是否强制置顶以及原因文本;本次调用来自 serviceTimeout(),只用到进程与原因文本两个参数。
// 代码位置 services/core/java/com/android/server/am/ActivityManagerService.java 第 4890—4906 行// 定义 ANR 处理入口final void appNotResponding(ProcessRecord app, ActivityRecord activity,// 接收父 ActivityRecord、是否强制置顶与 ANR 说明文本 ActivityRecord parent, boolean aboveSystem, final String annotation) {// 准备收集主进程附近的 pid 列表 ArrayList<Integer> firstPids = new ArrayList<Integer>(5);// 准备收集次要相关 pid 列表 SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);// 存在外部 ActivityController 时先询问处理意见 if (mController != null) {// 捕获远程调用异常 try {// 询问是否继续或立即杀掉进程 int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);// 返回负值且不是本进程时执行终止 if (res < 0 && app.pid != MY_PID) {// 以 anr 原因终止目标进程 app.kill("anr", true);// 结束终止分支 }// 捕获 Controller 失效的情况 } catch (RemoteException e) {// 清空失效的 Controller 引用 mController = null;// 通知 Watchdog 清除 ActivityController Watchdog.getInstance().setActivityController(null);// 结束异常处理 }// 结束 Controller 判断 }// 记录本次 ANR 发生时间 long anrTime = SystemClock.uptimeMillis();appNotResponding() 后续如何生成弹窗、如何抓取 traces,本文素材没有覆盖到,第 8.2 节把它登记为待核实项。
MainActivity.onCreate() 中阻塞 30.5 秒也会出现 ANR,但那条路径通常由输入分发或 Activity 生命周期监控发现。现象同为 ANR,不表示底层一定经过 ActiveServices.serviceTimeout()。判断来源要结合 ANR reason、traces 和 AMS 日志。
4.5 onStartCommand 返回值如何影响 stopIfKilled
handleServiceArgs() 把 onStartCommand() 的返回值送回 AMS。serviceDoneExecutingLocked(r, type, startId, res) 在 SERVICE_DONE_EXECUTING_START 分支中处理它。三种常用返回值对 stopIfKilled 的影响如下。
| 返回值 | serviceDoneExecutingLocked 中的处理 |
stopIfKilled |
进程被杀后的含义 |
|---|---|---|---|
START_NOT_STICKY |
findDeliveredStart(startId, true) 移除已交付项;仅当这是最后一个 startId 时设置 |
true |
没有新的显式启动请求时,不要求系统重建该启动态服务 |
START_STICKY |
移除已交付项 | false |
保留“被杀后仍需启动”的语义;重建时 Intent 可能为 null |
START_REDELIVER_INTENT |
findDeliveredStart(startId, false) 保留 StartItem,清零 deliveryCount 并增加完成次数 |
true |
保留待重新投递的 Intent,由记录本身驱动 redelivery;不能只凭 stopIfKilled 单字段理解 |
源码中 START_REDELIVER_INTENT 也把 stopIfKilled 设为 true,容易与 API 文档的“重新投递 Intent”产生表面冲突。关键在于对应的 StartItem 没有被移除。重启决策读取的是一组状态,不是单看这个布尔值。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1892—1909 行// 按 onStartCommand 返回值分支switch (res) {// 兼容模式与 START_STICKY 共用处理 case Service.START_STICKY_COMPATIBILITY:// 进入 START_STICKY 分支 case Service.START_STICKY: {// 移除已交付的启动项 r.findDeliveredStart(startId, true);// 被杀时仍保留启动需求 r.stopIfKilled = false;// 结束分支 break;// 结束 sticky 块 }// 进入 START_NOT_STICKY 分支 case Service.START_NOT_STICKY: {// 移除已交付的启动项 r.findDeliveredStart(startId, true);// 仅对最后一次启动设置停止标记 if (r.getLastStartId() == startId) {// 允许被杀后停止 r.stopIfKilled = true;// 结束最后一次启动判断 }// 结束分支 break;// 结束 not-sticky 块 }// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1911—1922 行// 进入重新投递 Intent 分支case Service.START_REDELIVER_INTENT: {// 查找但不移除已交付启动项 ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);// 确认启动项存在 if (si != null) {// 清零本轮投递次数 si.deliveryCount = 0;// 增加完成执行次数 si.doneExecutingCount++;// 允许结合保留的启动项决定后续行为 r.stopIfKilled = true;// 结束启动项判断 }// 结束分支 break;// 结束 redeliver 块}五、bindService
5.1 不能跨进程的 ServiceConnection
·
应用传给 bindService() 的 ServiceConnection 是普通 Java 接口对象。AMS 位于另一个进程,无法持有并直接调用这份对象引用。Framework 要把它包装成实现 IServiceConnection Binder 接口的对象,跨进程传递的是这个 Binder 端点。
ContextImpl.bindServiceCommon() 先调用 LoadedApk.getServiceDispatcher(),得到 IServiceConnection sd,随后才向 AMS 发起 bindService。这一步还带来另一个效果:Framework 保存原始 ServiceConnection 与代理的对应关系,便于去重、解绑和泄漏检查。
5.2 getServiceDispatcher 与 ServiceDispatcher/InnerConnection
LoadedApk 以 Context 为第一层键,以应用传入的 ServiceConnection 为第二层键保存 ServiceDispatcher。已有记录会复用并校验 Context 与 Handler;没有记录时创建新的包装器。
// 代码位置 core/java/android/app/LoadedApk.java 第 977—997 行// 定义 ServiceConnection 包装入口public final IServiceConnection getServiceDispatcher(ServiceConnection c,// 接收 Context、Handler 和绑定标志 Context context, Handler handler, int flags) {// 在连接映射锁内操作 synchronized (mServices) {// 初始化包装器 LoadedApk.ServiceDispatcher sd = null;// 按 Context 取得连接表 ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);// 连接表存在时尝试复用 if (map != null) {// 按原始 ServiceConnection 查询 sd = map.get(c);// 结束查询 }// 没有包装器时创建 if (sd == null) {// 创建 ServiceDispatcher sd = new ServiceDispatcher(c, context, handler, flags);// Context 首次绑定时创建二级表 if (map == null) {// 分配连接映射 map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();// 保存 Context 对应的映射 mServices.put(context, map);// 结束新表分支 }// 保存原对象到包装器的映射 map.put(c, sd);// 已存在时校验调用环境 } else {// 检查 Context 和 Handler 是否一致 sd.validate(context, handler);// 结束复用分支 }// 返回可跨进程的 IServiceConnection return sd.getIServiceConnection();// 退出连接映射锁 }// 结束方法}ServiceDispatcher 内部的 InnerConnection 继承 IServiceConnection.Stub,具备 Binder 服务端能力。它通过 WeakReference 指回 ServiceDispatcher,收到 connected() 后转交给包装器。
// 代码位置 core/java/android/app/LoadedApk.java 第 1067—1080 行// 定义 Binder 回调端点private static class InnerConnection extends IServiceConnection.Stub {// 弱引用外层 ServiceDispatcher final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;// 构造回调端点 InnerConnection(LoadedApk.ServiceDispatcher sd) {// 保存弱引用,避免单凭 Binder 端点强持有包装器 mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);// 结束构造 }// 接收 AMS 发回的连接结果 public void connected(ComponentName name, IBinder service) throws RemoteException {// 尝试取得 ServiceDispatcher LoadedApk.ServiceDispatcher sd = mDispatcher.get();// 包装器仍存在时继续 if (sd != null) {// 转交连接结果 sd.connected(name, service);// 结束非空判断 }// 结束回调 }// 结束内部类}
图 5 ServiceConnection 经 ServiceDispatcher 和 InnerConnection 变成 Binder 回调端点,再切回调用方 Handler。
IServiceConnection 是 android.app 包下的 AIDL 生成接口,它的 Stub/Proxy 分工与 5.7 节 UserAidl 那套结构相同。InnerConnection extends IServiceConnection.Stub 意味着这份实现留在调用方进程,跨进程传出去的只是它的 Binder 引用;AMS 在 publishServiceLocked() 里执行 c.conn.connected(r.name, service) 时拿到的是 IServiceConnection.Proxy,这次调用经 Binder 驱动回到调用方进程的 InnerConnection.connected()。Stub 与 Proxy 分处两端,两者的边界就是 bindService() 跨过的那道 Binder。
5.3 bindServiceLocked 登记了什么
AMS 的 bindService() 进入 ActiveServices.bindServiceLocked()。它同样调用 retrieveServiceLocked() 取得 ServiceRecord,随后通过 retrieveAppBindingLocked() 取得调用方与该 Service/Intent 的绑定记录,再创建 ConnectionRecord。
AppBindRecord 和 ConnectionRecord 分属两个粒度,后面的 b.connections.add(c)、b.client.connections.add(c) 里的 b 与 c 就是这个区别。ServiceRecord.retrieveAppBindingLocked()(ServiceRecord.java 第 363—377 行)以 IntentBindRecord 和调用方 ProcessRecord 为键,做的是查不到才建;同一个调用进程用同一个 Intent 反复 bindService(),取到的是同一个 AppBindRecord,它代表“这个进程对这个 Service 的这个绑定 Intent”的聚合状态。ConnectionRecord 则是 bindServiceLocked() 里每次调用都新建的对象(new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent)),对应一次具体的 bindService() 调用,也就是应用传入的那一个 ServiceConnection。同一个 AppBindRecord 的 connections 字段可以聚合多个 ConnectionRecord:同一个调用方对同一个 Service、同一个 Intent 用两个不同的 ServiceConnection 各绑一次,会得到两个 ConnectionRecord,共享同一个 AppBindRecord。
连接会写入多个容器:Service 侧的 s.connections、AppBindRecord.connections、调用方进程的 connections,以及 ActiveServices.mServiceConnections。这些索引服务于不同清理方向,解绑、Activity 销毁、调用进程死亡或 Service 进程死亡时都能定位关系。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 783—813 行// 取得调用方与 Service 的绑定记录AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);// 创建一条具体连接记录ConnectionRecord c = new ConnectionRecord(b, activity,// 保存 Binder 回调、标志及客户端信息 connection, flags, clientLabel, clientIntent);// 取得回调端点的 Binder 身份IBinder binder = connection.asBinder();// 查询 Service 侧同一 Binder 的连接列表ArrayList<ConnectionRecord> clist = s.connections.get(binder);// 首次出现时创建列表if (clist == null) {// 分配连接列表 clist = new ArrayList<ConnectionRecord>();// 写入 Service 侧映射 s.connections.put(binder, clist);// 结束新列表分支}// 加入 Service 侧列表clist.add(c);// 加入 AppBindRecordb.connections.add(c);// 加入调用进程的连接集合b.client.connections.add(c);// 查询全局连接表clist = mServiceConnections.get(binder);// 全局表没有该 Binder 时创建列表if (clist == null) {// 分配全局连接列表 clist = new ArrayList<ConnectionRecord>();// 写入全局索引 mServiceConnections.put(binder, clist);// 结束全局新列表分支}// 把连接加入全局索引clist.add(c);5.4 复用同一条启动链
当 flags 带 BIND_AUTO_CREATE,bindServiceLocked() 更新 lastActivity 后调用同一个 bringUpServiceLocked()。所以“绑定会不会创建 Service”的准确答案取决于标志和当前状态;在常见的 BIND_AUTO_CREATE 情况下,目标进程与 Service 尚不存在时,会复用第三章的进程创建和 scheduleCreateService() 链。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 815—820 行// 判断是否要求绑定时自动创建 Serviceif ((flags&Context.BIND_AUTO_CREATE) != 0) {// 刷新 Service 活动时间 s.lastActivity = SystemClock.uptimeMillis();// 复用 bringUpServiceLocked 启动链 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {// 启动失败时返回零 return 0;// 结束失败判断 }// 结束自动创建分支}启动态和绑定态可以同时存在。stopService() 只清除启动需求时,仍有自动创建式绑定的 Service 可能继续存活;unbindService() 清除最后一条绑定时,若 startRequested 仍为真,Service 也不会因此停止。
5.5 requestServiceBindingLocked 到 publishService
Service 进程可用后,requestServiceBindingLocked() 先登记执行状态,再调用 scheduleBindService()。目标进程的 handleBindService() 在非 rebind 分支执行 onBind(),取得业务 Binder,并通过 publishService() 发回 AMS。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1171—1180 行// 首次请求或 rebind 且存在客户端时投递if ((!i.requested || rebind) && i.apps.size() > 0) {// 捕获跨进程投递异常 try {// 标记 bind 生命周期正在执行 bumpServiceExecutingLocked(r, execInFg, "bind");// 提升目标进程状态 r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);// 投递绑定生命周期 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,// 附带进程状态 r.app.repProcState);// 首次绑定时记录请求已发出 if (!rebind) {// 避免重复发起相同绑定 i.requested = true;// 结束首次绑定分支 }// 标记曾经绑定 i.hasBound = true;// 代码位置 core/java/android/app/ActivityThread.java 第 2902—2911 行// 捕获 Binder 回报异常try {// 首次绑定走 onBind if (!data.rebind) {// 调用业务 Service 并取得 Binder IBinder binder = s.onBind(data.intent);// 把 Binder 发布给 AMS ActivityManagerNative.getDefault().publishService(// 携带 Service token、Intent 和 Binder data.token, data.intent, binder);// rebind 走单独回调 } else {// 调用 onRebind s.onRebind(data.intent);// 告诉 AMS rebind 执行完成 ActivityManagerNative.getDefault().serviceDoneExecuting(// 使用匿名执行完成类型 data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);// 结束 rebind 分支 }AMS 收到 publishService() 后,publishServiceLocked() 把 Binder 写入 IntentBindRecord,遍历与该 Intent 匹配的连接,调用 c.conn.connected(r.name, service)。这里的 c.conn 正是调用方进程创建的 InnerConnection Binder 端点。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 873—895 行// 构造发布 Intent 的比较键Intent.FilterComparison filter// 包装原 Intent = new Intent.FilterComparison(intent);// 查找对应的绑定记录IntentBindRecord b = r.bindings.get(filter);// 只处理尚未收到 Binder 的记录if (b != null && !b.received) {// 保存 Service 返回的 Binder b.binder = service;// 标记请求已发出 b.requested = true;// 标记 Binder 已收到 b.received = true;// 遍历 Service 的连接映射 for (int conni=r.connections.size()-1; conni>=0; conni--) {// 取得一组连接 ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);// 遍历组内连接 for (int i=0; i<clist.size(); i++) {// 取得单条连接 ConnectionRecord c = clist.get(i);// 跳过 Intent 不匹配的连接 if (!filter.equals(c.binding.intent.intent)) {// 继续下一条连接 continue;// 结束过滤分支 }// 捕获连接回调异常 try {// 跨进程回调调用方的 InnerConnection c.conn.connected(r.name, service);5.6 回调回到调用方主线程:RunConnection 与 doConnected
Binder 线程收到 InnerConnection.connected() 后,ServiceDispatcher.connected() 不会直接调用应用的 ServiceConnection。有 mActivityThread Handler 时,它投递 RunConnection,由指定线程执行 doConnected()。常规 Activity Context 使用主线程 Handler,因此 onServiceConnected() 回到调用方主线程。
// 代码位置 core/java/android/app/LoadedApk.java 第 1146—1152 行// 接收 InnerConnection 转来的连接结果public void connected(ComponentName name, IBinder service) {// 有目标 Handler 时切换线程 if (mActivityThread != null) {// 投递连接任务 mActivityThread.post(new RunConnection(name, service, 0));// 没有 Handler 时直接处理 } else {// 同步更新连接 doConnected(name, service);// 结束 Handler 分支 }// 结束方法}doConnected() 还会为新 Binder 注册 linkToDeath()。如果旧 Binder 存在,先调用 onServiceDisconnected();新 Binder 非空时再调用 onServiceConnected()。死亡监听使调用方能在服务端进程意外退出后清理本地连接状态。
// 代码位置 core/java/android/app/LoadedApk.java 第 1191—1204 行// 新 Binder 非空时建立连接信息if (service != null) {// 清除死亡状态 mDied = false;// 创建连接信息 info = new ConnectionInfo();// 保存 Binder info.binder = service;// 创建死亡监听器 info.deathMonitor = new DeathMonitor(name, service);// 捕获 Binder 已死亡异常 try {// 注册死亡通知 service.linkToDeath(info.deathMonitor, 0);// 保存活动连接 mActiveConnections.put(name, info);// Binder 已死时清理 } catch (RemoteException e) {// 移除无效连接 mActiveConnections.remove(name);// 结束本次处理 return;// 结束异常分支 }// 代码位置 core/java/android/app/LoadedApk.java 第 1217—1224 行// 存在旧连接时通知断开if (old != null) {// 回调应用的 ServiceConnection mConnection.onServiceDisconnected(name);// 结束旧连接分支}// 新 Binder 存在时通知连接成功if (service != null) {// 把 Binder 交给应用 mConnection.onServiceConnected(name, service);// 结束新连接分支}5.7 asInterface 代理
onServiceConnected() 参数类型是 IBinder。若服务通过 AIDL 定义接口,客户端调用 UserAidl.Stub.asInterface(service):本地 Binder 会返回本地接口,远程 Binder 则包装成 Proxy。业务代码随后调用的是 UserAidl,实际跨进程行为由生成代码完成。
// 代码位置 UserAidl.aidl 第 1—5 行// 声明 AIDL 包package com.example.service;// 定义跨进程用户接口interface UserAidl {// 读取用户名 String getUserName();// 读取口令 String getUserPassword();// 结束接口}下面摘出 AIDL 生成代码中决定“本地对象还是远程代理”的结构。实际生成文件还包含事务编号、onTransact() 与 Parcel 编解码,这些机械代码不影响本节判断。
// 代码位置 UserAidl.java 第 1—20 行// 定义生成的 Binder 接口public interface UserAidl extends android.os.IInterface {// 提供默认实现 public static class Default implements UserAidl {// 返回默认用户名 public String getUserName() { return null; }// 返回默认口令 public String getUserPassword() { return null; }// 返回默认 Binder public android.os.IBinder asBinder() { return null; }// 结束默认实现 }// 定义 Binder 服务端骨架 public static abstract class Stub extends android.os.Binder implements UserAidl {// 声明接口描述符 private static final java.lang.String DESCRIPTOR = "com.example.service.UserAidl";// 把 IBinder 转换成业务接口 public static UserAidl asInterface(android.os.IBinder obj) {// 空 Binder 直接返回空 if (obj == null) return null;// 查询同进程本地接口 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);// 类型匹配时复用本地对象 if (iin instanceof UserAidl) return (UserAidl) iin;// 跨进程时创建代理 return new Proxy(obj);// 结束转换方法 }// 结束 Stub }// 结束接口}演示 Service 返回 UserAidl.Stub 匿名实现。示例中的邮箱和口令已经替换为中性占位值。
// 代码位置 MessageService.java 第 1—31 行// 声明演示 Servicepublic class MessageService extends Service {// 定义日志标签 private static final String TAG = "MessageService";// 创建 AIDL Binder 实现 private final UserAidl.Stub mBinder = new UserAidl.Stub() {// 实现用户名读取 @Override public String getUserName() {// 返回中性邮箱占位值 return "user@example.com";// 结束用户名方法 }// 实现口令读取 @Override public String getUserPassword() {// 返回中性口令占位值 return "123456";// 结束口令方法 }// 结束 Binder 实现 };// 处理 Service 创建 @Override public void onCreate() {// 调用父类实现 super.onCreate();// 记录创建日志 Log.e(TAG, "onCreate");// 用阻塞复现 Service 执行超时 Thread.sleep(25000);// 结束创建回调 }// 处理启动参数 @Override public int onStartCommand(Intent intent, int flags, int startId) {// 记录启动日志 Log.e(TAG, "onStartCommand");// 返回非粘性策略 return START_NOT_STICKY;// 结束启动回调 }// 处理绑定 @Override public IBinder onBind(Intent intent) {// 返回 AIDL Binder return mBinder;// 结束绑定回调 }// 处理解绑 @Override public boolean onUnbind(Intent intent) {// 记录解绑日志 Log.e(TAG, "onUnbind");// 采用默认不 rebind 语义 return false;// 结束解绑回调 }// 结束 Service}
图 6 Binder 驱动以 binder_proc、handle、todo 与 wait 队列衔接写入、唤醒和读取过程。
Android 6.0 的 Binder 驱动通信可以用三个系统调用阶段定位:
binder_open为进程建立binder_proc。驱动内部用多棵红黑树维护节点、引用和缓冲区等结构;handle 0 约定指向 ServiceManager,其余 handle 由引用关系分配。binder_mmap建立 Binder 缓冲区的用户空间与内核空间映射。录屏登记的 6.0 讲解口径是只开一个物理页用于映射管理,并把一次完整往返按六次数据复制描述;这个计数依赖统计边界,本文只把它作为该版本材料的实现提示,不扩展成所有 Binder 版本的固定结论。binder_ioctl承载binder_write与binder_read。写入侧依据 handle 找到目标binder_proc,把工作项送入线程或进程 todo 队列并唤醒;读取侧先检查 todo,没有工作时进入 wait 队列等待。
从这层回看 Service 绑定,IServiceConnection、IApplicationThread、AIDL 的 UserAidl 都只是不同的 Binder 接口。驱动传递的是事务与 Binder 引用,调用方最终拿到的是本地 Stub 或远程 Proxy,取决于服务端是否与调用方处于同一进程。
六、进程没起来时的一次回环
6.1 mPendingServices 与 attachApplicationLocked
第三章在 startProcessLocked() 后把 ServiceRecord 放进 mPendingServices。新进程创建 ActivityThread 并通过 attachApplication 连回 AMS 后,ActivityManagerService.attachApplicationLocked() 调用 mServices.attachApplicationLocked(app, processName)。ActiveServices 遍历等待队列,按隔离进程记录,或按 uid 与进程名,挑出属于该进程的 Service。
匹配项先从 mPendingServices 删除,再调用 realStartServiceLocked()。这段回环保证 AMS 不会在 ApplicationThread 尚不可用时投递创建事务,也避免把另一个同名但 uid 不同的进程误当成目标。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 2026—2045 行// 处理新附着进程等待中的 Serviceboolean attachApplicationLocked(ProcessRecord proc, String processName)// 声明远程异常 throws RemoteException {// 记录是否处理了工作 boolean didSomething = false;// 仅在等待队列非空时遍历 if (mPendingServices.size() > 0) {// 保存当前候选记录 ServiceRecord sr = null;// 捕获真实启动的远程异常 try {// 遍历等待中的 Service for (int i=0; i<mPendingServices.size(); i++) {// 取得候选记录 sr = mPendingServices.get(i);// 隔离进程或 uid、进程名不匹配时跳过 if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid// 完成复合匹配条件 || !processName.equals(sr.processName))) {// 继续下一条 continue;// 结束不匹配分支 }// 从等待队列移除匹配记录 mPendingServices.remove(i);// 修正删除后的索引 i--;// 把 Service 包加入新进程 proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,// 关联进程统计 mAm.mProcessStats);// 回到真实启动阶段 realStartServiceLocked(sr, proc, sr.createdFromFg);// 标记本轮完成工作 didSomething = true;// 结束遍历 }这也是 startService 与带 BIND_AUTO_CREATE 的 bindService 共享启动链的证据:等待队列保存的是 ServiceRecord,并不按最初入口拆成两套进程启动机制。回到 realStartServiceLocked() 创建 Service 后,启动请求继续发送 args,绑定请求继续调用 requestServiceBindingLocked()。
七、stopService 与 unbindService
7.1 stopService:目标进程走到 onDestroy
ContextImpl.stopServiceCommon() 同样请求 AMS,由 ActiveServices 清除启动态需求,并在 Service 已无存活理由时投递停止。它与 startServiceCommon() 对称,只是把 Binder 调用换成 stopService(),并把负数返回码翻译成权限异常。
// 代码位置 core/java/android/app/ContextImpl.java 第 1265—1280 行// 定义 stopService 的公共实现private boolean stopServiceCommon(Intent service, UserHandle user) {// 捕获跨进程异常 try {// 校验 Service Intent validateServiceIntent(service);// 把 Intent 整理成可离开当前进程的状态 service.prepareToLeaveProcess();// 获取 AMS 代理并发起停止请求 int res = ActivityManagerNative.getDefault().stopService(// 传递应用线程、Intent、解析类型与用户标识 mMainThread.getApplicationThread(), service,// 补全参数并接收整型返回码 service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());// 负数返回码表示权限不足 if (res < 0) {// 抛出安全异常 throw new SecurityException(// 说明未获准停止 "Not allowed to stop service " + service);// 结束权限判断 }// 非零返回码表示确实停止了某个 Service return res != 0;// 捕获远程异常 } catch (RemoteException e) {// 转换成运行时异常 throw new RuntimeException("Failure from system", e);// 结束异常处理 }// 结束方法}目标进程的 ActivityThread.handleStopService() 按 token 从 mServices 删除 Java 对象,调用 onDestroy(),等待 QueuedWork 收尾,再回报 SERVICE_DONE_EXECUTING_STOP。
// 代码位置 core/java/android/app/ActivityThread.java 第 3035—3052 行// 处理停止 Service 消息private void handleStopService(IBinder token) {// 从目标进程实例表移除 Service Service s = mServices.remove(token);// 找到实例时执行销毁 if (s != null) {// 捕获生命周期异常 try {// 调用 Service.onDestroy s.onDestroy();// 取得 Service 基础 Context Context context = s.getBaseContext();// 只处理 ContextImpl if (context instanceof ContextImpl) {// 取得 Service 类名 final String who = s.getClassName();// 安排 Context 最终清理 ((ContextImpl) context).scheduleFinalCleanup(who, "Service");// 结束 Context 类型判断 }// 等待排队工作完成 QueuedWork.waitToFinish();// 捕获完成回报异常 try {// 回报 stop 生命周期完成 ActivityManagerNative.getDefault().serviceDoneExecuting(// 使用停止执行类型 token, SERVICE_DONE_EXECUTING_STOP, 0, 0);onDestroy() 是组件生命周期回调,不等于进程退出。进程中可能还有 Activity、Provider、Receiver、其他 Service、线程或 Binder 对象。AMS 会根据整体组件状态和内存压力调整进程优先级,不能从一次 stopService() 推导出 Linux 进程立即消失。
7.2 unbindService:isServiceNeeded 决定是否真正停止
unbindServiceLocked() 删除对应 ConnectionRecord。删除动作由 removeConnectionLocked() 执行,判据是 IServiceConnection 的 Binder 身份;同一调用方用多个 ServiceConnection 绑定时,这里按 Binder 逐条清空。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 912—931 行// 定义 unbindService 的锁内实现boolean unbindServiceLocked(IServiceConnection connection) {// 取得回调端点的 Binder 身份 IBinder binder = connection.asBinder();// 按 Binder 查询全局连接表 ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);// 找不到对应连接时直接失败 if (clist == null) {// 记录未找到连接 Slog.w(TAG, "Unbind failed: could not find connection for "// 附加 Binder 信息 + connection.asBinder());// 返回失败 return false;// 结束早退分支 }// 清除调用身份以执行系统侧工作 final long origId = Binder.clearCallingIdentity();// 捕获清理过程异常 try {// 逐条清空该 Binder 对应的连接列表 while (clist.size() > 0) {// 取列表中第一条连接 ConnectionRecord r = clist.get(0);// 从各张表中移除该连接 removeConnectionLocked(r, null, null);// 仍在原位时兜底移除,避免死循环 if (clist.size() > 0 && clist.get(0) == r) {// 记录异常状态 Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);// 强制移除 clist.remove(0);// 结束兜底分支 }关系变化后,ActiveServices 用 isServiceNeeded() 判断 Service 是否仍有存在理由。AOSP 6.0 的核心条件是 startRequested 为真,或者仍存在 BIND_AUTO_CREATE 连接。只有两者都不成立,系统才有条件执行 bring-down。
// 代码位置 services/core/java/com/android/server/am/ActiveServices.java 第 1647—1662 行// 定义 Service 是否仍需要存在的判断private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) {// 显式启动请求未清除时仍需要 if (r.startRequested) {// 直接判定需要 return true;// 结束启动请求判断 }// 调用方未提供绑定结论时现查 if (!knowConn) {// 查询是否存在自动创建式绑定 hasConn = r.hasAutoCreateConnections();// 结束现查分支 }// 存在自动创建式绑定时仍需要 if (hasConn) {// 判定需要 return true;// 结束绑定判断 }// 两个条件都不成立时不再需要 return false;// 结束方法}这个条件解释了常见的两种“停不下来”:调用过 startService() 又绑定时,只解绑仍保留启动态;只调用 stopService() 时,自动创建式绑定仍然保留绑定态。要完整释放同时处于两种状态的 Service,调用方需分别解除自己建立的状态。
7.3 线程、静态变量与 GC root
Service 实例从 ActivityThread.mServices 删除并完成 onDestroy() 后,普通对象仍要等到不可达才会被 GC。若业务自行创建的线程还在运行,线程对象及其引用链可以成为 GC root 的可达路径;静态字段也由类加载器关联。Framework 不会替业务代码终止这些线程,也不会把所有静态字段清空。
因此,“Service 已销毁”“Service 对象可回收”“目标进程退出”是三个层级:
| 层级 | 直接判据 | 不能据此推出 |
|---|---|---|
| 组件生命周期结束 | onDestroy() 已返回,AMS 完成停止记账 |
进程已经退出 |
| Java 对象可回收 | 没有任何强引用路径 | GC 已经立刻执行 |
| 进程退出 | 进程被系统或应用终止 | 静态状态会以某种业务协议持久化 |
若 onDestroy() 后仍看到工作继续,排查重点应放在线程、线程池、Handler 消息、Binder 回调和静态引用,而不是假设 Framework 没有调用销毁回调。
八、小结
8.1 dumpsys、logcat 与 am 命令
startService:ContextImpl.startServiceCommon → AMS.startService → ActiveServices.startServiceLocked → retrieveServiceLocked → bringUpServiceLocked / realStartServiceLocked → scheduleCreateService → handleCreateService → onCreate → scheduleServiceArgs → handleServiceArgs → onStartCommand → serviceDoneExecuting bindService:ContextImpl.bindServiceCommon → LoadedApk.getServiceDispatcher → AMS.bindService → ActiveServices.bindServiceLocked → retrieveServiceLocked + ConnectionRecord → bringUpServiceLocked / realStartServiceLocked → scheduleBindService → handleBindService → onBind → publishService → c.conn.connected → InnerConnection → RunConnection → doConnected → onServiceConnected设备或模拟器上可以用下面的入口交叉验证。命令中的包名和组件名需要替换成实际应用。
# 查看 AMS 当前保存的 ServiceRecord、启动态与绑定关系adb shell dumpsys activity services com.example.service # 过滤 Service 生命周期、AMS 超时和 ANR 相关日志adb logcat -v threadtime | grep -E "ActivityManager|ActiveServices|MessageService|ANR|am_anr" # 从 shell 发起显式启动adb shell am startservice -n com.example.service/.MessageService # 从 shell 停止显式 Serviceadb shell am stopservice -n com.example.service/.MessageService验证启动复用时,可以连续执行两次 am startservice,对照日志中 onCreate() 与 onStartCommand() 的次数,再从 dumpsys 查看 startRequested、最近 startId 与连接信息。验证绑定链需要由测试 Activity 调用 bindService(),因为 shell 的 am 命令不能替应用提供一个可接收 onServiceConnected() 的 ServiceConnection 对象。
分析 ANR 时,不要只截取弹窗。至少保留以下证据:
- ANR reason,确认是
executing service还是输入分发、Broadcast 等其他类型; traces中主线程栈,确认阻塞点位于哪个生命周期或锁等待;- ANR 前后的 AMS 与业务日志,寻找
scheduleCreateService、onCreate、serviceDoneExecuting对应的时间边界; dumpsys activity services中的 executing、startRequested 和 connections 状态。
核心问题由此可以落到具体状态变化上:两条入口共享 ContextImpl → AMS → ActiveServices → ActivityThread 的调度骨架;startService 维护启动项并投递 CREATE_SERVICE、SERVICE_ARGS,bindService 额外维护连接记录并投递 BIND_SERVICE。同一段耗时是否形成 Service ANR,取决于它是否落在 bumpServiceExecutingLocked() 与对应完成回报之间,以及进程采用 20 秒还是 200 秒窗口,而不取决于方法名字看起来是否“属于 Service”。
本文由 AgentWorkFlow 生成