AIDL 数据类型详解之 C++ 篇

AIDL 中的数据结构

在上文我们说到 AIDL 中支持的数据类型,这里我们再回顾一下:

在 Java 层,AIDL 支持以下多种数据类型:

  • Java 编程语言中的所有的基本类型(如 int、long、char、boolean 等)
  • String 与 CharSequence
  • List:List 中的所有元素必须是 AIDL 支持的数据类型,生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList
  • Map:Map 中的所有元素必须是 AIDL 支持的数据类型,不支持泛型 Map(如 Map<String,Integer> 形式的 Map),生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap
  • 生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap。
  • Parcelable 类型

Native 层支持类似的数据类型,这些类型与 Java 层的对应关系如下:

Native 示例程序

接下来我们就来写一个演示 AIDL 数据类型的 Native层 示例程序:

首先我们在 device/jelly/rice14/ 目录下创建如下的文件与文件夹

bash 复制代码
AIDLCppTypeDemo
├── Android.bp
├── com
│   └── yuandaima
│       ├── IHello.aidl
│       ├── Student.aidl
│       ├── Student.cpp
│       └── Student.h
├── HelloClient.cpp
└── HelloServer.cpp

其中的 Student.aidl Student.h Student.cpp 是我们自定义的数据类型,在 AIDL 中自定义数据类型需要继承 Parcelable

cpp 复制代码
//Student.h
#ifndef _COM_YUANDAIMA_STUDENT_H
#define _COM_YUANDAIMA_STUDENT_H

#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <binder/Status.h>
#include <utils/RefBase.h>
#include <vector>

using namespace std;
using namespace android;

namespace com {
namespace yuandaima {

    class Student : public Parcelable {
        public:
            Student();
            virtual ~Student();
            
            virtual status_t writeToParcel(Parcel* out) const;
            virtual status_t readFromParcel(const Parcel* in); 

            void setAge(int32_t age);
            int32_t getAge();
            void setName(String16 name);
            String16 getName();
        private:
            String16 mName;
            int32_t mAge;
    };

}
}
#endif

// Studeng.cpp
#include "Student.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;
using namespace android;

namespace com {
namespace yuandaima {

    Student::Student() {
        this->setAge(20);
        this->setName(String16("jack"));
    }

    Student::~Student() {
    }

    status_t Student::writeToParcel(Parcel* out) const {
        status_t err;
        err = out->writeString16(mName);
        if (err != NO_ERROR) {
            return err;
        }
        err = out->writeInt32(mAge);
        if (err != NO_ERROR) {
            return err;
        }
        return NO_ERROR;
    }

    status_t Student::readFromParcel(const Parcel* in) {
        status_t err;
        err = in->readString16(&mName);
        if (err != NO_ERROR) {
            return err;
        }
        err = in->readInt32(&mAge);
        if (err != NO_ERROR) {
            return err;
        }

        return NO_ERROR;
    }

}}

在 aidl 中我们只需要简单声明即可:

java 复制代码
//Student.aidl
package com.yuandaima;

parcelable Student cpp_header "com/yuandaima/Student.h";

与 Java 不同的是,我们需要指定对应的头文件。

IHelloService.aidl 中声明了我们 binder 服务的对外接口:

java 复制代码
package com.yuandaima;

import com.yuandaima.Student;

interface IHello
{
    void hello();
    int sum(int x, int y);
    int printList(in List<String> strs);
	int printMap(in Map maps);
    int printStudent(in Student student);
}

接着我们编译 aidl 文件:

bash 复制代码
# 源码目录下
source build/envsetup.sh
# 选择合适的 product
lunch

# 进入项目目录下
# -I 用于指定我们的在哪里查找 import
aidl-cpp -I . com/yuandaima/IHello.aidl ./ ./IHello.cpp

编译后,生成了对应的 h 和 cpp 文件,整个项目结构如下:

bash 复制代码
AIDLCppTypeDemo
├── Android.bp
├── com
│   └── yuandaima
│       ├── BnHello.h
│       ├── BpHello.h
│       ├── IHello.aidl
│       ├── IHello.h
│       ├── Student.aidl
│       ├── Student.cpp
│       └── Student.h
├── HelloClient.cpp
├── HelloServer.cpp
└── IHello.cpp

接着服务端类和服务端主程序 HelloServer.cpp

cpp 复制代码
#define LOG_TAG "aidl_cpp"

#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"

using namespace android;

class IHelloServer : public com::yuandaima::BnHello
{
public:
    binder::Status hello() override
    {
        ALOGI("hello");
        return binder::Status();
    }

    binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
    {
        ALOGI("server: sum: %d + %d", v1, v2);
        *_aidl_return = v1 + v2;
        return binder::Status();
    }

    binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
    {
        for (const auto & str : strs)
        {
            ALOGI("%s", String8(str).c_str());
        }

        *_aidl_return = 1;
        return binder::Status();
    }

    binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
    {
        for(auto it : maps){
            std::string val;
            it.second.getString(&val);
            ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
        }
        *_aidl_return = 1;
        return binder::Status();
    }

};

int main(int argc, char const *argv[])
{
    defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

接着完成客户端程序 HelloClient.cpp :

java 复制代码
#define LOG_TAG "aidl_cpp"

#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"

using namespace android;

class IHelloServer : public com::yuandaima::BnHello
{
public:
    binder::Status hello() override
    {
        ALOGI("hello");
        return binder::Status();
    }

    binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
    {
        ALOGI("server: sum: %d + %d", v1, v2);
        *_aidl_return = v1 + v2;
        return binder::Status();
    }

    binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
    {
        for (const auto & str : strs)
        {
            ALOGI("%s", String8(str).c_str());
        }

        *_aidl_return = 1;
        return binder::Status();
    }

    binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
    {
        for(auto it : maps){
            std::string val;
            it.second.getString(&val);
            ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
        }
        *_aidl_return = 1;
        return binder::Status();
    }

};

int main(int argc, char const *argv[])
{
    defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

最后编译测试:

bash 复制代码
# 项目目录下执行单编
mm
# 回到系统源码目录
adb push out/target/product/rice14/system/bin/IHelloTypeServer /data/local/tmp

adb push out/target/product/rice14/system/bin/IHelloTypeClient /data/local/tmp

# 进入模拟器 shell 环境
adb shell
cd /data/local/tmp

# 执行服务端
./IHelloTypeServer & 
# 执行客户端
./IHelloTypeClient 

接着查看log:

bash 复制代码
logcat | grep aidl_cpp

08-07 10:57:44.372  2824  2825 I aidl_cpp: hello
08-07 10:57:44.372  2824  2825 I aidl_cpp: server: sum: 1 + 2
08-07 10:57:44.373  2824  2825 I aidl_cpp: Hello Binder
08-07 10:57:44.373  2824  2825 I aidl_cpp: key is test, value is 
08-07 12:04:57.181 10985 10986 I aidl_cpp: hello
08-07 12:04:57.182 10985 10986 I aidl_cpp: server: sum: 1 + 2
08-07 12:04:57.182 10985 10986 I aidl_cpp: Hello Binder
08-07 12:04:57.182 10985 10986 I aidl_cpp: key is test, value is 
08-07 12:07:48.053 11334 11335 I aidl_cpp: hello
08-07 12:07:48.053 11334 11335 I aidl_cpp: server: sum: 1 + 2
08-07 12:07:48.054 11334 11335 I aidl_cpp: Hello Binder
08-07 12:07:48.054 11334 11335 I aidl_cpp: key is test, value is 
08-07 12:07:48.054 11334 11335 I aidl_cpp: name is jack, age is 20

从 log 可以看出我们的程序指针成功了。

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

相关推荐
律宏阔26 分钟前
Android 车载 USB 开发笔记:USB Host、USB 串口、USB-CAN、HID 与系统 API
android
律宏阔36 分钟前
Android 车载串口开发笔记:UART、RS232、RS485、串口配置与数据通信
android
YF02112 小时前
遥控器APP端自动重连方案
android
执明wa2 小时前
Android Studio 打包 APK
android·ide·android studio
waiting9711183 小时前
Ubuntu 26.04 + Android14安装与编译教程
android·linux·ubuntu
hunterandroid3 小时前
Android 测试全景:从单元测试到 UI 自动化的完整实践
android·前端
蜡台3 小时前
Kotlin 五大作用域函数详解|let/run/apply/also/with 选型指南+实战避坑
android·java·kotlin
2401_833269304 小时前
Android registerForActivityResult详解
android
Carson带你学Android4 小时前
9月 Android Drop:Gemini 终于不只是聊天助手了
android·ai编程
Sagittarius_A*4 小时前
【LitCTF2026】lit_ezsql
android·java·数据库