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

相关推荐
消失的旧时光-19431 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
Jinkxs1 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&1 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
LDORntKQH1 小时前
基于深度强化学习的混合动力汽车能量管理策略 1.利用DQN算法控制电池和发动机发电机组的功率分配 2
android
冬奇Lab1 小时前
Android 15 ServiceManager与Binder服务注册深度解析
android·源码·源码阅读
2501_916008893 小时前
深入解析iOS机审4.3原理与混淆实战方法
android·java·开发语言·ios·小程序·uni-app·iphone
独行soc5 小时前
2026年渗透测试面试题总结-20(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
常利兵5 小时前
2026年,Android开发已死?不,它正迎来黄金时代!
android
Risehuxyc5 小时前
备份三个PHP程序
android·开发语言·php
Doro再努力15 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim