第1章:系统服务框架核心:ServiceManager、Binder机制、系统服务注册与获取

各位同学,咱们今天聊点硬核的。

Android系统服务框架,说白了就是整个系统的"中枢神经"。你手机里每个App能打电话、发定位、弹通知,背后全是这套框架在撑着。我当年刚接触AOSP时,最头疼的就是搞不清ServiceManager和Binder到底怎么配合的。今天咱们就把这块彻底掰开揉碎。

1.1 ServiceManager:系统服务的"电话本"

ServiceManager是什么?我习惯叫它"服务大管家"。所有系统服务启动后,第一件事就是找它报到。

核心职责:

  • 维护一个服务名到Binder引用的映射表
  • 提供注册(addService)和查询(getService)接口
  • 服务端和客户端通过它完成"握手"

你想想看,如果没有ServiceManager,每个App想用个服务都得满世界找——这得多乱?

我记得在某个项目里,我们自定义了一个硬件监控服务。一开始没注意注册时机,结果服务还没注册完,客户端就来查询了,直接返回null。嗯,这种坑我踩过不止一次。

1.2 Binder机制:跨进程通信的"高速公路"

Binder,Android里最核心的IPC机制。说实话,它比Linux传统的Socket、共享内存要优雅得多。

为什么这么说?因为Binder把进程间通信伪装成了本地调用。你调用一个远程服务的方法,感觉就像调用本地对象一样。这背后是Binder驱动在默默干活。

我的经验:调试Binder问题时,多看看/proc/binder/下的文件。里面记录了所有Binder节点的状态,特别有用。

Binder通信的四个关键角色:

角色 说明
Client 发起请求的一方,比如某个App
Server 提供服务的一方,比如SystemServer
ServiceManager 服务注册中心,类似DNS
Binder Driver 内核模块,负责数据拷贝和线程管理

我曾经遇到过一个诡异的问题:某个服务偶尔调用超时。查了两天才发现,是Binder线程池被耗尽了。每个服务默认只有16个Binder线程,并发一高就容易卡死。

避坑指南:自定义服务时,如果预期并发量很大,记得在服务端调用BinderInternal.setMaxThreads()增加线程数。我曾经因为没设这个,线上出了P0故障。

1.3 系统服务注册:从代码到运行

咱们来看看一个服务是怎么注册进去的。以我自定义的MyCustomService为例:

// 服务端代码
public class MyCustomService extends IMyCustomService.Stub {
    private static final String TAG = "MyCustomService";
    private Context mContext;

    public MyCustomService(Context context) {
        mContext = context;
    }

    @Override
    public String getCustomData() {
        return "这是自定义服务返回的数据";
    }
}

// 在SystemServer中注册
public final class SystemServer {
    private void startOtherServices() {
        try {
            // 实例化服务
            MyCustomService service = new MyCustomService(mSystemContext);
            // 注册到ServiceManager
            ServiceManager.addService("my_custom_service", service);
            Slog.i(TAG, "MyCustomService 注册成功");
        } catch (Throwable e) {
            Slog.e(TAG, "注册失败: " + e.getMessage());
        }
    }
}

这里有个细节:addService的第二个参数是IBinder类型。我们的Stub类本质上就是一个Binder对象。ServiceManager拿到这个引用后,会把它存到内部的一个哈希表里。

我建议你在注册时加上try-catch。为什么?因为ServiceManager本身也是个服务,如果它还没启动完成,addService会抛异常。我在Android 8.0上就遇到过这个时序问题。

1.4 系统服务获取:客户端视角

服务注册好了,客户端怎么用?很简单:

// 客户端代码
IBinder binder = ServiceManager.getService("my_custom_service");
if (binder != null) {
    IMyCustomService service = IMyCustomService.Stub.asInterface(binder);
    String data = service.getCustomData();
    Log.d(TAG, "获取到数据: " + data);
} else {
    Log.e(TAG, "服务未找到,请检查服务是否已注册");
}

这里有个关键点:asInterface方法。它会把Binder引用转换成我们熟悉的接口类型。如果客户端和服务端在同一个进程,它直接返回服务端对象;如果跨进程,它返回一个代理对象(Proxy)。

说白了,这就是Binder的"透明性"——你根本不用关心对方在哪个进程里。

性能建议:频繁调用getService会有性能开销。我一般会在客户端缓存这个Binder引用,只在服务重启时重新获取。你可以监听ServiceManager.onServiceAdded回调来更新缓存。

1.5 实战中的那些坑

讲了这么多,我总结几个实际项目中容易踩的坑:

  • 服务注册顺序:依赖其他服务的服务,一定要等依赖就绪后再注册。我见过有人把网络服务注册在Wi-Fi服务之前,结果网络服务初始化时直接崩溃。
  • Binder对象泄漏:服务不再使用时,记得调用linkToDeath注册死亡通知。否则客户端会一直持有无效引用。
  • 权限检查:自定义服务一定要加权限验证。我见过一个开发者的自定义服务没加权限,结果被第三方App随意调用,数据全泄露了。

嗯,说到权限,我再啰嗦一句。Android 10之后,对非系统级服务的管控更严了。如果你的服务不想被普通App调用,记得在onTransact里加checkCallingPermission检查。

1.6 小结

今天咱们把ServiceManager、Binder机制、服务注册与获取这三块串了一遍。说白了,ServiceManager是"目录",Binder是"通道",服务注册就是"上架",获取就是"下单"。理解了这个模型,后面咱们讲自定义系统服务就轻松多了。

下一章,我会带大家手写一个完整的系统服务——从AIDL定义到SystemServer注册,再到客户端调用。到时候咱们再细聊那些代码层面的细节。

课后思考:如果让你设计一个支持热插拔的系统服务,ServiceManager的注册逻辑需要做哪些改动?欢迎在评论区讨论。