第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 避坑指南

最后分享几个我踩过的坑:

  1. Service 被系统杀死后自动重启:onStartCommand 返回 START_STICKY 时,Service 被杀死后会重启。但如果你返回的是 START_NOT_STICKY,就不会重启。我有个项目因为返回了 START_STICKY,导致 Service 在后台频繁重启,耗电严重。
  2. bindService 的泄漏问题:在 Activity 销毁时一定要调用 unbindService,否则 Service 会一直持有 Activity 的引用,导致内存泄漏。我习惯在 onDestroy 里统一处理。
  3. 多进程 Service 的坑:如果 Service 运行在独立进程,onBind 返回的 Binder 对象必须是跨进程可用的。我见过有人直接返回了一个匿名内部类,结果跨进程调用时直接崩溃。
  4. 前台 Service 的通知:Android 8.0 以后,后台 Service 限制很严。如果你的 Service 需要长时间运行,必须使用前台 Service 并显示通知。否则系统会直接杀掉你的 Service。

嗯,Service 的启动和绑定流程,说白了就是 AMS 和 App 进程之间的一场「舞蹈」。AMS 负责调度,App 进程负责执行。理解了 ServiceRecord 和 ActiveServices 这两个核心类,你就能掌握 Service 的整个生命周期了。