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 层实现的
Binder
public 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
值来保证客户端调用的函数可以准确调用到服务端对应的函数。