18、Binder 的匿名服务:什么是匿名 Binder 服务?如何传递与使用?
聊完了实名 Binder 服务,咱们今天来聊聊它的“孪生兄弟”——匿名 Binder 服务。
说实话,我刚接触 Android 的时候,对“匿名服务”这个概念挺懵的。实名服务好歹有个名字,你通过 ServiceManager 一查就能找到。匿名服务呢?它连个名字都没有,那别人怎么找到它?
后来我在一个多媒体项目中真正用到了它,才恍然大悟。说白了,匿名 Binder 服务就是不需要注册到 ServiceManager 的 Binder 服务。它通过 Binder 传递机制,直接把服务端的 Binder 引用发给客户端。
18.1 什么是匿名 Binder 服务?
先看个直观的定义:
- 实名 Binder 服务:注册到 ServiceManager,有全局唯一的名字,任何进程都能通过名字查到。
- 匿名 Binder 服务:不注册到 ServiceManager,没有名字,只能通过 Binder 传递来获取引用。
你想想看,ServiceManager 就像一个电话本。实名服务就是上面登记了号码的公司,你查一下就能打过去。匿名服务呢?它不登记,但你朋友可以直接把它的号码发给你。
我在项目中遇到过这样一个场景:一个应用需要给另一个应用传递一个临时的回调接口。这个回调接口只在这个会话中有效,用完就作废。如果把它注册成实名服务,还得起个名字、注册、用完再注销,太麻烦了。匿名 Binder 服务正好解决这个问题。
核心要点:匿名 Binder 服务的生命周期由持有它的 Binder 引用决定。当所有引用都被释放时,服务自动销毁。
18.2 匿名 Binder 服务的传递机制
匿名 Binder 服务的传递,依赖的是 Binder 驱动的一个核心能力——Binder 节点迁移。
当一个进程把 Binder 对象作为参数或返回值传递给另一个进程时,Binder 驱动会在目标进程中创建一个新的 Binder 引用,指向同一个 Binder 节点。
这个过程我画了张图,你看完就明白了:
18.3 如何传递匿名 Binder 服务?
传递匿名 Binder 服务,最常用的方式就是通过已有实名服务的接口。举个例子:
// 服务端:定义回调接口
interface IMyCallback {
void onResult(int code, String data);
}
// 服务端:实名服务接口中增加注册回调的方法
interface IMyService {
void registerCallback(IMyCallback callback);
void doSomething();
}
// 服务端:实现类
public class MyService extends IMyService.Stub {
private IMyCallback mCallback;
@Override
public void registerCallback(IMyCallback callback) {
// 这里接收到的 callback 就是匿名 Binder 服务
mCallback = callback;
}
@Override
public void doSomething() {
// 执行某些操作后,通过回调通知客户端
if (mCallback != null) {
try {
mCallback.onResult(200, "操作成功");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
// 客户端:实现回调接口
public class MyCallback extends IMyCallback.Stub {
@Override
public void onResult(int code, String data) {
// 这里运行在 Binder 线程池中
Log.d("Callback", "收到结果: code=" + code + ", data=" + data);
}
}
// 客户端:注册回调
IMyService service = IMyService.Stub.asInterface(binder);
service.registerCallback(new MyCallback());
service.doSomething();
个人经验:我习惯在注册回调时,把回调对象用 WeakReference 包一层。这样可以避免服务端持有了客户端的强引用,导致客户端无法被 GC 回收。曾经有个线上 bug,就是因为回调没释放,导致 Activity 泄漏了。
18.4 匿名 Binder 服务的典型应用场景
在实际开发中,匿名 Binder 服务主要有这几个用途:
| 场景 | 说明 | 示例 |
|---|---|---|
| 回调接口 | 服务端需要异步通知客户端 | Service 向 Activity 报告进度 |
| 临时会话 | 只在某个操作期间有效的服务 | 文件传输会话、临时授权 |
| 私有服务 | 不想暴露给所有进程的服务 | 系统内部组件通信 |
| 动态代理 | 需要动态创建和销毁的服务 | 插件化框架中的组件通信 |
18.5 匿名 Binder 服务的生命周期管理
这里有个坑,我得提醒你注意。
匿名 Binder 服务的生命周期,完全由 Binder 驱动中的引用计数决定。每个进程持有一个 Binder 引用,驱动就会增加一次计数。当所有进程都释放了引用,驱动就会销毁对应的 Binder 节点。
我曾经在项目中遇到过一个诡异的问题:客户端注册了回调,但服务端调用回调时总是抛 RemoteException。排查了半天,发现是客户端进程被杀了,但服务端不知道,还在傻傻地调用。
避坑指南:
- 调用匿名 Binder 服务时,一定要捕获 RemoteException
- 服务端可以注册 DeathRecipient 监听客户端的死亡通知
- 不要在匿名 Binder 服务中持有 Activity 或 Context 的强引用
18.6 匿名 vs 实名:怎么选?
我个人的选择标准很简单:
- 需要全局访问 → 实名 Binder 服务(注册到 ServiceManager)
- 只在特定进程间通信 → 匿名 Binder 服务(通过已有接口传递)
- 临时回调 → 匿名 Binder 服务(用完即弃)
- 系统级服务 → 实名 Binder 服务(需要统一管理)
说白了,实名服务是“广播找人”,匿名服务是“点对点传话”。没有谁好谁坏,看场景选就对了。
总结一下:匿名 Binder 服务是 Android 进程间通信中非常灵活的一种方式。它不需要 ServiceManager 的参与,通过 Binder 传递机制直接在进程间建立通信通道。理解它的原理和生命周期,能帮你写出更健壮、更高效的跨进程通信代码。
公众号:蓝海资料掘金营,微信deep3321