Android C++ Binder 的两种实现方式

Binder 机制是 Android 中使用最广泛的进程间通信机制,借助 Binder 开发者可以方便的开发各种实现应用间信息传输、分享的应用。对于 Android 应用开发者来说,开发都是基于 Java 语言进行的,但其实 Android 系统提供了实现 C 语言层的 Binder 的方式,分别是记住 libbinder.solibbinder_ndk.so 的两种方式。

官方文档如下:

AIDL 后端 | Android Open Source Project

NdkBinder | Android NDK | Android Developers

libbinder.so 的方式

libbinder.so 是Android 系统源码编译后产生的库文件,并没有共享到 sdk 中,这就意味着这种方式,一般需要 Android 系统源码,不适合于第三方应用开发。

libbinder_ndk.so 的方式

libbinder_ndk.sondk 中自带的,这意味着在配置好 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;
};

样例中定义了 encryptdecrypt 两个函数。

实现服务端

服务端需要继承 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 层编程使用。示例如下:

  1. 定义 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 相同

  1. 定义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;
    }

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

相关推荐
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android