第4章 Service启动与绑定源码分析:startService与bindService的完整流程,ServiceRecord与ActiveServices的管理
Service 这玩意儿,说白了就是 Android 里跑后台任务的组件。很多新手容易把 Service 和线程搞混,其实它俩完全不是一回事。Service 是运行在主线程的,你如果在里面做耗时操作,照样 ANR。我当年刚入行时就踩过这个坑,以为 Service 自带子线程,结果线上出了不少 ANR 问题。
今天咱们就深入源码,看看 startService 和 bindService 到底是怎么玩的。ServiceRecord 和 ActiveServices 这两个类,是理解 Service 生命周期的关键。
4.1 startService 的完整流程
先看个最简单的调用:
Intent intent = new Intent(this, MyService.class);
startService(intent);
这行代码背后,到底发生了什么?
嗯,咱们一步步来。从 ContextImpl 开始:
// ContextImpl.java
@Override
public ComponentName startService(Intent service) {
// 这里会检查权限,处理 UserId 等
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
// 关键:通过 ActivityManager.getService() 获取 AMS 的代理
// 然后发起跨进程调用
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()),
requireForeground, getOpPackageName(), user.getIdentifier());
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这里有个关键点:ActivityManager.getService() 返回的是 IActivityManager 的 Binder 代理。说白了,就是通过 Binder 机制告诉 AMS:「嘿,我要启动一个 Service」。
AMS 那边收到请求后,会调用 ActiveServices.startServiceLocked()。ActiveServices 这个类,是 AMS 专门用来管理 Service 的。我习惯把它理解为「Service 的大管家」。
核心数据结构:ServiceRecord
每个正在运行或即将运行的 Service,在 AMS 里都对应一个 ServiceRecord 对象。它记录了 Service 的所有信息:
- 包名、类名、进程信息
- 当前状态(创建、启动、绑定等)
- 启动次数、绑定记录
- Intent 过滤器等
看看 ServiceRecord 的部分关键字段:
// ServiceRecord.java
final class ServiceRecord extends Binder implements ComponentName.WithComponentName {
final ActivityManagerService ams;
final ComponentName name; // Service 的组件名
final String packageName; // 所属包名
final Intent.FilterComparison intent;
final ServiceInfo serviceInfo; // 从 Manifest 解析的信息
ProcessRecord app; // 运行在哪个进程
boolean delayedStop; // 是否延迟停止
boolean startRequested; // 是否已请求启动
boolean delayed; // 是否被延迟启动
int lastStartId; // 最后一次启动的 ID
// 绑定相关的
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings;
final ArrayMap<IBinder, ConnectionRecord> connections;
}
回到流程。AMS 的 startService 最终会走到 ActiveServices.startServiceInnerLocked():
// ActiveServices.java
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service,
ServiceRecord r, boolean callerFg, boolean addToStarting) {
// 关键:处理 Service 的状态
ServiceRecord cur = smap.getServiceByName(r.name);
// 如果 Service 还没启动,需要先创建
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return null;
}
return r.name;
}
bringUpServiceLocked 这个方法,我建议你重点看。它负责把 Service 拉起来,如果进程还没创建,它会先创建进程:
// ActiveServices.java
private String bringUpServiceLocked(ServiceRecord r, int intentFlags,
boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired) {
// 如果 Service 已经有进程了,直接发送创建和启动命令
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
// 没有进程?那就先启动进程
// 这里会调用 AMS 的 startProcessLocked
if (!isolated) {
app = ams.startProcessLocked(r.processName, r.appInfo, ...);
}
// 进程启动后,会回调回来,最终调用 realStartServiceLocked
return null;
}
进程启动后,AMS 会通过 ApplicationThread 回调到应用进程,最终调用 ActivityThread.handleCreateService():
// ActivityThread.java
private void handleCreateService(CreateServiceData data) {
// 1. 加载 Service 类,创建实例
Service service = (Service) cl.loadClass(data.info.name).newInstance();
// 2. 创建 ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
// 3. 调用 attach,建立关联
service.attach(context, this, data.info.name, ...);
// 4. 调用 onCreate
service.onCreate();
// 5. 把 Service 对象缓存起来
mServices.put(data.token, service);
}
创建完 Service 后,紧接着会调用 handleServiceArgs,触发 onStartCommand:
// ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
// 调用 onStartCommand,把 startId 传过去
int res = s.onStartCommand(data.args, data.flags, data.startId);
// 把结果告诉 AMS
ActivityManager.getService().serviceDoneExecuting(data.token, ...);
}
}
个人经验:我在项目中遇到过一个问题,Service 的 onCreate 里做了太多初始化,导致 ANR。后来我养成了一个习惯:onCreate 只做轻量级初始化,耗时操作放到 onStartCommand 里用子线程处理。你想想看,onCreate 是在主线程执行的,一旦阻塞,整个应用都卡住了。
4.2 bindService 的完整流程
bindService 和 startService 最大的区别是什么?
startService 是「启动即走」,Service 自己运行,和调用者没有直接交互。而 bindService 是「绑定交互」,调用者可以通过 Binder 对象直接调用 Service 的方法。
看调用方式:
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
这里的 connection 是一个 ServiceConnection 对象,它有两个回调:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定成功,拿到 Binder 对象
mBinder = IMyService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 绑定断开
mBinder = null;
}
};
bindService 的流程,同样从 ContextImpl 开始:
// ContextImpl.java
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(),
Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, UserHandle user) {
// 把 ServiceConnection 包装成 IServiceConnection
// 因为跨进程需要 Binder 对象
IServiceConnection sd = new InnerConnection(conn);
// 跨进程调用 AMS
int res = ActivityManager.getService().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()), sd, flags,
getOpPackageName(), user.getIdentifier());
return res != 0;
}
AMS 收到请求后,会调用 ActiveServices.bindServiceLocked:
// ActiveServices.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) {
// 1. 查找或创建 ServiceRecord
ServiceRecord s = mServices.getServiceByName(service.getComponent());
if (s == null) {
// 如果 Service 还没注册,需要先解析
ServiceLookupResult res = retrieveServiceLocked(service, ...);
s = res.record;
}
// 2. 创建 ConnectionRecord
// 每个绑定关系对应一个 ConnectionRecord
ConnectionRecord b = new ConnectionRecord(s, connection, ...);
// 3. 把绑定关系加到 ServiceRecord 里
s.connections.add(b);
// 4. 如果设置了 BIND_AUTO_CREATE,且 Service 还没启动
if ((flags & Context.BIND_AUTO_CREATE) != 0) {
s.app.thread.scheduleBindService(s, ...);
}
// 5. 如果 Service 还没运行,需要先拉起来
if (!s.isRunning()) {
bringUpServiceLocked(s, ...);
}
return 1;
}
这里有个重要的数据结构:ConnectionRecord。它记录了谁绑定了哪个 Service,以及绑定时的 flag:
// ConnectionRecord.java
final class ConnectionRecord {
final ServiceRecord service; // 绑定的 Service
final IServiceConnection conn; // 客户端的回调接口
final int flags; // 绑定标志
final ProcessRecord client; // 哪个进程绑定的
final String clientPackageName; // 哪个包绑定的
boolean serviceDead; // Service 是否已死亡
}
当 Service 进程启动后,AMS 会调用 realStartServiceLocked,然后通过 ApplicationThread 回调到应用进程:
// ActivityThread.java
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
// 调用 Service 的 onBind 方法
// 返回 Binder 对象
IBinder binder = s.onBind(data.intent);
// 把 Binder 对象通过 AMS 返回给客户端
ActivityManager.getService().publishService(data.token, data.intent, binder);
}
}
AMS 收到 publishService 后,会遍历所有 ConnectionRecord,把 Binder 对象分发给每个客户端:
// ActiveServices.java
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
// 遍历所有等待绑定的连接
for (ConnectionRecord c : r.connections) {
if (c.binding.intent.app != null) {
// 通过 Binder 回调客户端的 onServiceConnected
c.conn.connected(r.name, service, false);
}
}
}
注意:bindService 有个坑。如果你在 Activity 的 onCreate 里调用 bindService,但 Service 还没启动,这时候 onServiceConnected 不会立即回调。它会等 Service 启动完成后才回调。我曾经在项目里遇到一个问题:在 onCreate 里 bindService,然后在 onServiceConnected 里做 UI 更新,结果发现有时候 UI 没更新。后来排查发现,是因为 Service 启动太慢,onServiceConnected 回调时 Activity 已经 onResume 了,但某些 View 还没初始化完成。
4.3 ServiceRecord 与 ActiveServices 的管理
ActiveServices 是 AMS 里管理 Service 的核心类。它维护了几个重要的数据结构:
// ActiveServices.java
class ActiveServices {
// 按 UserId 分组的 Service 映射
SparseArray<ServiceMap> mServiceMap;
// 延迟停止的 Service 列表
ArrayList<ServiceRecord> mPendingStops;
// 正在重启的 Service
ArrayList<ServiceRecord> mRestartingServices;
// 已销毁的 Service
ArrayList<ServiceRecord> mDestroyingServices;
// Service 的最大启动次数,防止无限重启
static final int MAX_SERVICE_INACTIVITY = 60 * 60 * 1000; // 1小时
}
ServiceMap 内部维护了按 Intent 和 ComponentName 的映射:
// ActiveServices.java
static class ServiceMap {
// 按 ComponentName 查找
final ArrayMap<ComponentName, ServiceRecord> mServicesByName;
// 按 Intent 查找
final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent;
final int mUserId;
}
ServiceRecord 的生命周期状态变化,我整理了一张表:
| 状态 | 触发条件 | 说明 |
|---|---|---|
| INIT | ServiceRecord 创建 | 初始状态,还没启动 |
| STARTED | startService 调用 | Service 已启动,onStartCommand 已调用 |
| BOUND | bindService 调用 | Service 已绑定,onBind 已调用 |
| STARTED_BOUND | 既 start 又 bind | 两种方式同时存在 |
| DESTROYING | stopService/unbindService | 正在销毁中 |
| DESTROYED | onDestroy 执行完毕 | 已销毁,等待回收 |
这里有个细节:当 Service 同时被 start 和 bind 时,它的生命周期会变复杂。必须所有 start 都 stop,所有 bind 都 unbind,Service 才会真正销毁。我见过不少新手在这个地方栽跟头。
核心要点:
- startService 和 bindService 是两条独立的路径
- ServiceRecord 是 AMS 管理 Service 的核心数据结构
- ActiveServices 负责 Service 的创建、启动、绑定、销毁全流程
- 跨进程通信通过 Binder 实现,AMS 是中间人
- Service 运行在主线程,耗时操作必须开子线程
4.4 避坑指南
最后分享几个我踩过的坑:
- Service 被系统杀死后自动重启:onStartCommand 返回 START_STICKY 时,Service 被杀死后会重启。但如果你返回的是 START_NOT_STICKY,就不会重启。我有个项目因为返回了 START_STICKY,导致 Service 在后台频繁重启,耗电严重。
- bindService 的泄漏问题:在 Activity 销毁时一定要调用 unbindService,否则 Service 会一直持有 Activity 的引用,导致内存泄漏。我习惯在 onDestroy 里统一处理。
- 多进程 Service 的坑:如果 Service 运行在独立进程,onBind 返回的 Binder 对象必须是跨进程可用的。我见过有人直接返回了一个匿名内部类,结果跨进程调用时直接崩溃。
- 前台 Service 的通知:Android 8.0 以后,后台 Service 限制很严。如果你的 Service 需要长时间运行,必须使用前台 Service 并显示通知。否则系统会直接杀掉你的 Service。
嗯,Service 的启动和绑定流程,说白了就是 AMS 和 App 进程之间的一场「舞蹈」。AMS 负责调度,App 进程负责执行。理解了 ServiceRecord 和 ActiveServices 这两个核心类,你就能掌握 Service 的整个生命周期了。