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

相关推荐
雨白7 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹8 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭11 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日11 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安12 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑12 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟16 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体