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

相关推荐
Dnelic-1 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen3 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年11 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿13 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神14 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛14 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法15 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter16 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快17 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl18 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5