Binder 机制是 Android 中使用最广泛的进程间通信机制,借助 Binder 开发者可以方便的开发各种实现应用间信息传输、分享的应用。对于 Android 应用开发者来说,开发都是基于 Java 语言进行的,但其实 Android 系统提供了实现 C 语言层的 Binder 的方式,分别是记住 libbinder.so 和 libbinder_ndk.so 的两种方式。
官方文档如下:
AIDL 后端 | Android Open Source Project
NdkBinder | Android NDK | Android Developers
libbinder.so 的方式
libbinder.so 是Android 系统源码编译后产生的库文件,并没有共享到 sdk 中,这就意味着这种方式,一般需要 Android 系统源码,不适合于第三方应用开发。
libbinder_ndk.so 的方式
libbinder_ndk.so 是 ndk 中自带的,这意味着在配置好 ndk 后,可以像开发普通第三方应用一样,开发 C 语言层的 binder 应用。
定义义接口
定义接口一般要继承 ndk:ICInterface ,示例如下:
#include <android/binder_ibinder.h>
#include <android/binder_interface_utils.h>
#include <android/binder_auto_utils.h>
class IEncryptor : public ndk::ICInterface
{
public:
virtual binder_status_t encrypt(char * src, int length, char* out, int &error) = 0;
virtual binder_status_t decrypt(char * src, int length, char* out, int &error) = 0;
};
样例中定义了 encrypt 和 decrypt 两个函数。
实现服务端
服务端需要继承 ndk::BnCInterface,并实现 createBinder 函数,来生成服务端的实例。服务端接口定义如下:
#include <android/binder_ibinder.h>
#include <android/binder_interface_utils.h>
#include <android/binder_auto_utils.h>
class BnEncryptServer : public ndk::BnCInterface<IEncryptor>{
public:
ndk::SpAIBinder createBinder() override;
binder_status_t encrypt(char *src, int length, char *out, int &error) override;
binder_status_t decrypt(char *src, int length, char *out, int &error) override;
};
服务端的接口实现样例如下:
binder_status_t IEncryptor_Class_onTransact(AIBinder* binder, transaction_code_t code,
const AParcel* in, AParcel* out){
LOGI( "IEncryptor_Class_onTransact ");
binder_status_t stat = STATUS_FAILED_TRANSACTION;
uid_t uid = AIBinder_getCallingUid();
pid_t pid = AIBinder_getCallingPid();
std::shared_ptr<IEncryptor> cipher = std::static_pointer_cast<IEncryptor>(ndk::ICInterface::asInterface(binder));
if(cipher != nullptr){
std::cout << "Hello world!!!" << std::endl;
}
switch (code) {
case TRANSACTION_ENCRYPT: {
int32_t valueIn;
int32_t valueOut;
stat = AParcel_readInt32(in, &valueIn);
if (stat != STATUS_OK) break;
int error;
stat = cipher->encrypt(nullptr, 0, nullptr, error);
if (stat != STATUS_OK) break;
stat = AParcel_writeInt32(out, valueOut);
break;
}
case TRANSACTION_DECRYPT: {
int error;
stat = cipher->decrypt(nullptr, 0, nullptr, error);
break;
}
}
return stat;
}
const char* ENCRYPTOR_DESCRIPTOR = "com.sdt.aidl.IAidl";
ndk::SpAIBinder inline BnEncryptServer::createBinder() {
LOGI( "BnEncryptServer::createBinder ");
AIBinder_Class* IEncryptor = defineClass(ENCRYPTOR_DESCRIPTOR, IEncryptor_Class_onTransact);
LOGI( "BnEncryptServer::createBinder define class ");
AIBinder *binder = AIBinder_new(IEncryptor, static_cast<void*>(this));
LOGI( "BnEncryptServer::createBinder AIBinder_new ");
return ndk::SpAIBinder(binder);
}
binder_status_t BnEncryptServer::encrypt(char *src, int length, char *out, int &error) {
LOGI( "BnEncryptServer::encrypt ");
return STATUS_OK;
}
binder_status_t BnEncryptServer::decrypt(char *src, int length, char *out, int &error) {
LOGI( "BnEncryptServer::decrypt ");
return STATUS_OK;
}
服务端实例的生成一般分为如下两步:
- 通过
defineClass生成BnBinder的类。在defineClass函数中需要传递transaction函数,用来根据code确定处理函数。 - 通过
AIBinder_new生成BnBinder的实例。
实现客户端
客户端一般继承 ndk::BpCInterface ,并实现定义的函数接口。在函数的实现中,通过 AParcel 来传递参数到服务端,通过AIBinder_transact向服务端发起函数请求。其样例代码如下:
class BpEncryptor : public ndk::BpCInterface<IEncryptor>
{
public:
virtual binder_status_t encrypt(char * src, int length, char* out, int &error) {
LOGI( "BpEncryptor: encrypt ");
binder_status_t stat = STATUS_OK;
AParcel* parcelIn;
stat = AIBinder_prepareTransaction(asBinder().get(), &parcelIn);
if (stat != STATUS_OK) return stat;
stat = AParcel_writeInt32(parcelIn, length);
if (stat != STATUS_OK) return stat;
stat = AParcel_writeCharArray(parcelIn, reinterpret_cast<const char16_t *>(src), length);
if (stat != STATUS_OK) return stat;
stat = AParcel_writeInt32(parcelIn, length);
if (stat != STATUS_OK) return stat;
ndk::ScopedAParcel parcelOut;
stat = AIBinder_transact(asBinder().get(), TRANSACTION_ENCRYPT, &parcelIn, parcelOut.getR(), 0 /*flags*/);
if (stat != STATUS_OK) return stat;
int32_t size = 0;
stat = AParcel_readInt32(parcelOut.get(), &size);
if (stat != STATUS_OK) return stat;
return stat;
}
virtual int decrypt(char * src, int length, char* out, int &error) override{
LOGI( "BpEncryptor: decrypt ");
ndk::ScopedAParcel parcelOut;
binder_status_t stat = STATUS_OK;
AParcel* parcelIn;
stat = AIBinder_prepareTransaction(asBinder().get(), &parcelIn);
if (stat != STATUS_OK) return stat;
stat = AIBinder_transact(asBinder().get(), TRANSACTION_DECRYPT, &parcelIn, parcelOut.getR(), 0 /*flags*/);
return stat;
}
};
使用
实现客户端和服务端后, 一般需要启动服务端,并将服务端的 binder 加入到 ServiceManager 中,但从目前的 NDK 接口中,未发现类似的函数接口。想想也合理,ndk 一般是用于第三方应用的开发,所以不提供接口将 binder 对象添加到 ServiceManager 中也合理。
ndk 提供了接口,方便 C 语言层和 Java 层 Binder 对象的互相转换,因此可以将 C 语言层定义的 Binder 对象转换到 Java 语言层,供 Java 层编程使用。示例如下:
-
定义 Java 层接口,即
AIDL文件(忽略接口参数)interface IAidl {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int encrypt();
int decrypt();
}
确保 C 层定义的 binder 对象的 Description 与 IAidl 生成的Java类的 Description 相同
-
定义native 函数返回 C 层实现的
Binderpublic class Native {
static {
System.loadLibrary("native-lib");
}
public static String TAG = "AIDL";public static native IBinder getBinder();}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_sdt_aidl_Native_getBinder(JNIEnv *env, jclass clazz) {
LOGI("Java_com_sdt_aidl_Native_getBinder");
BnEncryptServer *server = new BnEncryptServer();
jobject result = AIBinder_toJavaBinder(env, server->asBinder().get());
LOGI("Java_com_sdt_aidl_Native_getBinder : %p" , result );
return result;
} -
实现 Service 返回 Binder
public class AidlService extends Service {
private static String TAG = Native.TAG;
@Nullable
@Override
public IBinder onBind(Intent intent) {
IBinder result = Native.getBinder();
Log.d(TAG, "onBind " + result);
return result;
}
}
几句话说说 Binder 机制
binder 机制能够运行的核心在于其驱动在内核中开辟一块共享内存,可以在不同进程中共享。
传递的参数是通过parcel 来进行封包/拆包,以此来确保传递的参数在不同进程间可以相同,这也是其并不能支持所有类型数据的原因。
至于调用函数,则是为每个函数分配一个整型的 Transaction code,通过这个Code值来保证客户端调用的函数可以准确调用到服务端对应的函数。