在 Android 系统开发中,Binder 作为核心的进程间通信(IPC)机制,承担着系统各组件间交互的重要职责。实际开发中经常会遇到需要在 Native(C++)层调用 Java 层 Binder 服务的场景,比如性能敏感模块需要在 Native 层实现,但又需要访问 Java 层的系统服务。
本文将深入探讨两种实现 Native 层调用 Java Binder 服务的方案:
这两种方案各有适用场景,第一种适合快速验证和简单调用,第二种则是更规范、更可维护的生产级解决方案。我们将从原理到实践,完整呈现这两种方案的实现细节。
首先创建如下项目结构:
code复制NativeCallJavaServiceSimple/
├── Android.bp
└── CppClient.cpp
CppClient.cpp 的核心代码如下:
cpp复制#include <binder/IServiceManager.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
using namespace android;
int main(void) {
// 1. 获取服务管理器
sp<IServiceManager> sm = defaultServiceManager();
// 2. 获取目标服务(服务名为"hello")
sp<IBinder> ibinder = sm->getService(String16("hello"));
// 3. 准备数据包
Parcel data, reply;
if(ibinder != NULL) {
// 4. 写入接口描述符
static String16 descriptor = String16("com.yuandaima.IHelloService");
data.writeInterfaceToken(descriptor);
// 5. 发起远程调用
ibinder->transact(IBinder::FIRST_CALL_TRANSACTION + 0, data, &reply, 0);
// 6. 读取返回结果
int result = reply.readInt32();
ALOGI("transact result : %d\n", result);
return 0;
}
}
获取 ServiceManager:
defaultServiceManager() 获取全局的 IServiceManager 实例获取目标服务:
getService() 方法根据服务名获取 IBinder 对象准备 Parcel 数据包:
data 用于写入调用参数reply 用于接收返回结果写入接口描述符:
writeInterfaceToken() 写入接口的完整类名发起远程调用:
transact() 方法执行实际的跨进程调用FIRST_CALL_TRANSACTION + 0 表示调用第一个方法处理返回结果:
bash复制source build/envsetup.sh
lunch xxx
mm
bash复制adb push out/target/product/rice14/system/bin/CppClient /data/local/tmp
adb push out/target/product/rice14/system/framework/BinderServer.jar /data/local/tmp
bash复制adb shell
cd /data/local/tmp
export CLASSPATH=/data/local/tmp/BinderServer.jar
app_process /data/local/tmp com.yuandaima.Server &
./CppClient
优点:
缺点:
首先需要定义 AIDL 接口文件:
code复制device/jelly/rice14/NativeCallJavaService/com/yuandaima/IHelloService.aidl
内容示例:
aidl复制package com.yuandaima;
interface IHelloService {
void hello();
int sum(int a, int b);
}
使用 aidl-cpp 工具生成桥接代码:
bash复制aidl-cpp com/yuandaima/IHelloService.aidl ./ ./IHelloService.cpp
生成的代码包括:
更新后的 CppClient.cpp:
cpp复制#include "com/yuandaima/IHelloService.h"
using namespace android;
using namespace com::yuandaima;
int main() {
// 获取服务
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("hello"));
// 转换为接口代理
sp<IHelloService> service = IHelloService::asInterface(binder);
if(service != nullptr) {
// 调用方法
service->hello();
int result = service->sum(3, 4);
ALOGI("Sum result: %d", result);
}
return 0;
}
Android.bp 需要包含生成的代码:
bp复制cc_binary {
name: "CppClient",
srcs: ["CppClient.cpp", "IHelloService.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
],
}
类型安全:
自动序列化:
接口清晰:
维护性好:
Native 调用 Java Binder 服务的完整流程:
服务注册:
客户端查询:
方法调用:
服务处理:
结果返回:
不同语言间的类型转换:
| Java 类型 | Native 类型 | AIDL 类型 |
|---|---|---|
| int | int32_t | int |
| String | String16 | String |
| List | std::vector | List |
| Parcelable | Parcelable | interface |
关键线程注意事项:
服务找不到:
权限问题:
接口不匹配:
序列化错误:
减少跨进程调用:
异步调用:
连接管理:
bash复制logcat | grep -E 'Binder|IHelloService'
bash复制adb shell su root cat /sys/kernel/debug/tracing/trace_pipe | grep binder
bash复制adb shell service list
处理自定义类型:
实现回调机制:
服务管理策略:
在实际项目中,我推荐优先使用 AIDL 方案,虽然初期配置稍复杂,但长期来看能显著提高代码的可维护性和稳定性。特别是在大型项目中,明确的接口定义和自动生成的桩代码能有效降低跨团队协作的沟通成本。