46. 系统服务:SystemServer 进程如何利用 Binder 提供系统服务?

嗯,这个问题问得很到位。SystemServer 是 Android 系统的“大管家”,几乎所有你叫得上名字的系统服务——ActivityManagerService、WindowManagerService、PackageManagerService——都跑在这个进程里。那它到底是怎么通过 Binder 把这些服务“发布”出去,让其他 App 能调用的呢?

说白了,就是一套“注册-发布-调用”的流程。我当年刚接触这部分源码时,觉得挺绕的。后来自己动手写过一个小型系统服务,才真正搞明白。今天咱们就把这层窗户纸捅破。

SystemServer 的启动入口

SystemServer 进程由 Zygote fork 出来,入口在 SystemServer.main() 方法。它干的第一件事就是启动系统服务。

// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    // 1. 准备主线程 Looper
    Looper.prepareMainLooper();

    // 2. 加载 native 库
    System.loadLibrary("android_servers");

    // 3. 创建系统上下文
    createSystemContext();

    // 4. 启动各项服务
    startBootstrapServices();   // 引导服务
    startCoreServices();        // 核心服务
    startOtherServices();       // 其他服务

    // 5. 进入消息循环
    Looper.loop();
}

你看,它把服务分成了三类:引导服务、核心服务、其他服务。这个分层是有讲究的——有些服务必须在最前面启动,比如 Installer(负责安装 APK),没有它后面啥也干不了。

服务是如何注册到 ServiceManager 的?

每个系统服务启动后,都会调用 ServiceManager.addService() 把自己注册进去。我举个例子,看看 ActivityManagerService 是怎么注册的:

// 在 startBootstrapServices() 中
mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class);
// 注册到 ServiceManager
ServiceManager.addService(Context.ACTIVITY_SERVICE, mActivityManagerService);

这里有个细节:ServiceManager.addService() 其实是一个 Binder 调用。它向 servicemanager 进程(一个独立的 native 进程)发送一条 ADD_SERVICE 事务。servicemanager 内部维护了一张哈希表,key 是服务名称(比如 "activity"),value 是 Binder 引用的句柄。

核心流程:

  1. SystemServer 创建服务对象(如 AMS)
  2. 调用 ServiceManager.addService(name, binder)
  3. servicemanager 将 (name, handle) 存入哈希表
  4. 客户端通过 ServiceManager.getService(name) 获取 Binder 代理

Binder 服务对象的创建方式

系统服务的 Binder 对象创建,通常有两种方式。我个人更推荐第二种,因为它更清晰。

方式 说明 典型例子
继承 Binder 类 直接继承 android.os.Binder,实现 onTransact() 老版本的服务,如 VibratorService
使用 AIDL 定义 .aidl 接口,自动生成 Stub 类 大多数现代服务,如 IActivityManager

我记得有一次排查一个系统服务 ANR 问题,发现对方用的是继承 Binder 的方式,onTransact() 里直接做了耗时操作。嗯,这就是典型的“图省事”埋下的坑。用 AIDL 的话,默认会在 Binder 线程池里处理,反而能避免这种问题。

客户端如何获取服务?

客户端 App 调用 getSystemService() 时,最终会走到 ServiceManager.getService()。这个过程我画了一张图,你一看就明白:

SystemServer 服务注册与客户端获取流程 客户端 App getSystemService() ServiceManager servicemanager 进程 SystemServer AMS / WMS 等服务 ① getService("activity") ② 返回 Binder 代理 ③ addService("activity", binder) 客户端 App 持有 Binder 代理 AMS 服务 Binder 线程池处理 ④ 直接 Binder 调用(跨进程) 关键点: • 服务注册是一次性的,发生在 SystemServer 启动阶段 • 客户端获取服务后,后续调用直接与 SystemServer 通信,不再经过 ServiceManager

你看,客户端拿到 Binder 代理后,后续的调用就直接走 Binder 驱动了,不再经过 ServiceManager。这也是 Binder 设计的高明之处——ServiceManager 只负责“引荐”,不参与实际通信。

服务注册的底层细节

咱们再深入一层。ServiceManager.addService() 最终会调用到 BinderInternal.addService(),这是一个 native 方法。它向 Binder 驱动发送 BC_TRANSACTION 命令,code 是 ADD_SERVICE_TRANSACTION

// frameworks/native/libs/binder/IServiceManager.cpp
virtual status_t addService(const String16& name, const sp<IBinder>& service) {
    Parcel data, reply;
    data.writeString16(name);
    data.writeStrongBinder(service);  // 将 Binder 对象扁平化写入
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err;
}

这里有个关键点:writeStrongBinder() 会把 Binder 对象的引用传递给 servicemanager。Binder 驱动会在内核中为这个服务创建一个 binder_node,并分配一个 handle。servicemanager 拿到 handle 后,存到自己的服务列表里。

避坑指南:

我曾经遇到过一个诡异的问题:某个系统服务注册成功了,但客户端调用时总是返回 null。查了半天,发现是服务注册时传的 Binder 对象是一个临时变量,被 GC 回收了。嗯,记住:注册到 ServiceManager 的 Binder 对象必须保持强引用,否则 Binder 驱动会认为服务已死亡,自动清理掉 handle。

系统服务的线程模型

SystemServer 启动后,会创建多个 Binder 线程。默认情况下,Binder 驱动会为每个进程分配 16 个 Binder 线程(上限可配置)。这些线程专门用来处理 incoming 的 Binder 调用。

我建议你记住这个原则:不要在 Binder 线程里做耗时操作。为什么?因为 Binder 线程池是共享的,一个服务卡住了,其他服务的调用也得排队等着。我之前排查过一个 ANR,就是某个系统服务的 onTransact() 里做了数据库查询,把整个 Binder 线程池堵死了。

注意事项:

  • 耗时操作请另起线程,或者使用 Handler 抛到工作线程
  • SystemServer 的主线程是 Looper 线程,用于处理 AMS 等服务的内部消息
  • Binder 调用默认是同步的,客户端会阻塞等待返回

服务死亡通知机制

系统服务也是会挂的。如果 SystemServer 中的某个服务进程崩溃了(虽然这种情况极少),Binder 驱动会检测到并通知所有持有该服务代理的客户端。客户端可以注册 DeathRecipient 来监听:

// 客户端注册死亡通知
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        // 服务挂了,尝试重连
        Log.w(TAG, "服务挂了,尝试重新获取");
        // 重新调用 getService()
    }
};
binder.linkToDeath(dr, 0);

这个机制在系统服务中很常用。比如 ActivityManagerService 如果挂了(虽然几乎不可能),Launcher 会收到死亡通知,然后尝试重新获取服务引用。

总结一下

SystemServer 利用 Binder 提供系统服务的核心思路,其实就是“注册-发现-调用”三步走。注册阶段把服务对象交给 ServiceManager 托管,发现阶段客户端通过名称查找,调用阶段直接走 Binder 驱动进行跨进程通信。

我个人觉得,理解这个流程的关键在于搞清楚两个角色:ServiceManager 是“黄页”,Binder 驱动是“邮局”。黄页帮你找到人,邮局负责送信。一旦你拿到了对方的地址,后续通信就不需要再查黄页了。

嗯,如果你正在开发自己的系统服务,记住我刚才说的:保持 Binder 对象的强引用,不要在 Binder 线程里做耗时操作,注册死亡通知。这三点做到位,基本不会出大问题。