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

相关推荐
千里马学框架5 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone6 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc6 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo8 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077008 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼9 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone10 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen10 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone10 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui