第3章 Service端Binder实现与注册:继承Binder类、重写onTransact、注册到ServiceManager与导出Service
本章聚焦Binder通信体系中Service端的核心实现环节。Service端需要完成四个关键步骤:继承Binder类(或原生BBinder)获得IPC能力,重写onTransact处理客户端请求,注册到ServiceManager让客户端可查询,以及导出Service使跨进程访问生效。这四个步骤构成Binder服务端骨架,理解其实现细节是掌握跨进程调用的基础。
3.1 原理剖析:Binder服务端角色与职责
在Binder机制中,Service端是实际提供功能的进程。每个Service端对应一个Binder实体对象(通常继承android.os.Binder或原生BBinder),该对象拥有唯一的handle,由Binder驱动分配。ServiceManager维护一张名称 → Binder引用的表,允许客户端通过名称获取远程Binder代理。Service端通过addService将自身注册到ServiceManager,同时通过Android的权限机制(如AndroidManifest中的android:exported或SEAndroid策略)控制外部访问。
关键概念表
| 组件 | 职责 | 与本章关系 |
|---|---|---|
| Binder实体 | 接收IPC请求并执行 | 通过继承Binder/BBinder创建 |
| onTransact | 分发请求代码,解析Parcel数据 | 需重写此方法处理业务逻辑 |
| ServiceManager | 全局服务注册表 | 调用addService注册服务名称 |
| 导出Service | 使服务对外部进程可见 | 系统服务依赖selinux,应用服务依赖exported属性 |
3.2 实现流程
- 继承Binder类:在Java端继承
android.os.Binder,在C++端继承BBinder。原生实现需实现onTransact方法。 - 重写onTransact:根据
code(请求标识整数)分发处理,从dataParcel读取参数,将结果写入replyParcel。 - 创建实例并注册到ServiceManager:调用
defaultServiceManager()->addService(String16("my_service"), serviceBinder)(C++)或ServiceManager.addService("my_service", binder)(Java)。 - 导出Service:系统服务需在
Android.bp或rc文件中声明,并配置SEAndroid策略;应用服务在AndroidManifest中设置android:exported="true"。
3.3 核心代码实现
3.3.1 C++原生Binder实现(系统服务典型)
// MyService.h
#include <binder/BBinder.h>
#include <binder/Parcel.h>
#include <binder/IServiceManager.h>
using namespace android;
class MyService : public BBinder {
public:
MyService() : BBinder() {}
virtual status_t onTransact(uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags) override;
};
status_t MyService::onTransact(uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags) {
switch (code) {
case 1: { // 自定义获取字符串
int32_t param = data.readInt32();
reply->writeInt32(param * 2);
return NO_ERROR;
}
case 2: { // 获取服务描述
reply->writeCString("Hello from MyService");
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
3.3.2 注册到ServiceManager
// main.cpp
int main(int argc, char** argv) {
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
sp<MyService> service = new MyService();
sm->addService(String16("my_service"), service);
// 挂起,保持服务运行
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
3.3.3 Java端实现(应用服务)
public class MyService extends android.app.Service {
private final IBinder mBinder = new MyBinder();
private class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data,
Parcel reply, int flags) throws RemoteException {
switch (code) {
case 1:
int input = data.readInt();
reply.writeInt(input * 2);
return true;
case 2:
reply.writeString("Hello from Java Service");
return true;
default:
return super.onTransact(code, data, reply, flags);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
在AndroidManifest.xml中导出Service:
<service android:name=".MyService"
android:exported="true">
<intent-filter>
<action android:name="com.example.ACTION_MY_SERVICE" />
</intent-filter>
</service>
3.4 调试方法
- 查看注册的服务:
adb shell service list列出所有已注册服务,检查是否有my_service。 - 验证onTransact返回值:若客户端调用返回
FAILED_TRANSACTION,检查onTransact返回错误码或未处理code。 - 日志跟踪:在onTransact中加入
ALOGD打印code与参数。 - Binder统计信息:
cat /d/binder/transactions或dumpsys binderservice --binder-domain。
3.5 部署与验证
- 将编译得到的可执行文件(如
my_service)push到设备/system/bin/。 - 赋予执行权限:
adb shell chmod 755 /system/bin/my_service。 - 后台启动:
adb shell /system/bin/my_service &。 - 验证注册:
adb shell service check my_service应返回Service my_service: found。
注意:系统服务需在
init.rc中定义为service,并添加seclabel,否则可能受SEAndroid限制无法注册成功。
3.6 常见错误与规避
| 错误现象 | 根因 | 解决方案 |
|---|---|---|
onTransact后客户端收到DEAD_OBJECT | 服务进程崩溃或未正确挂起 | 确保调用了joinThreadPool;检查Binder线程池启动 |
service list看不到服务 | addService调用失败或权限不足 | 检查logcat中ServiceManager的错误;确认进程有ADD_SERVICE权限 |
客户端调用返回FAILED_TRANSACTION | onTransact未处理该code或返回错误 | 在onTransact中增加default分支调用父类;打印调用code |
| SEAndroid拒绝注册 | 服务进程没有对应的service_manager域 | 添加SEPolicy:allow my_service service_manager:service_manager add; |
| Binder内存泄露 | Parcel未正确清理,尤其是大型数据 | 避免在onTransact中持有Parcel引用;使用局部变量 |
3.7 性能与稳定性优化
- 控制事务大小:Binder传输数据量建议小于512KB,否则触发额外内存拷贝。
- 同步vs异步:复杂耗时操作应使用
TF_ONE_WAY标志(客户端不等待返回),或另开线程处理。 - 减少onTransact中的锁竞争:若多个客户端并发请求,内部数据结构需线程安全。
- 使用服务缓存:避免频繁addService/removeService,服务注册后应常驻。
- 引用计数管理:C++端使用
sp<>智能指针,防止Binder对象提前释放。
关键实践提醒:在重写
onTransact时,务必确保所有路径都调用基类的BBinder::onTransact处理未识别的code,否则Binder协议校验会失败导致进程被kill。
3.8 本章小结
Service端Binder实现的核心在于正确继承并注册:继承Binder/BBinder提供IPC框架,重写onTransact定义业务接口,addService注册到全局命名空间,导出Service赋予外部访问权限。这四个步骤环环相扣,任何一个环节出错都会导致跨进程通信失败。掌握本章内容后,即可构建可被任意客户端调用的Binder服务,为后续的代理端实现和复杂IPC模式打下基础。