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

相关推荐
洞见不一样的自己1 小时前
android 常用方法
android
暗碳2 小时前
华为麦芒5(安卓6)termux记录 使用ddns-go,alist
android·linux
seven27292 小时前
Android MQTT关于断开连接disconnect报错原因
android·mqtt·disconnect报错
Maplee6 小时前
Compose 转场动画之 Transition
android·前端
weixin_482565537 小时前
Android IC读写器安卓小程序 3
android·小程序
hvinsion8 小时前
Python PDF批量加密工具
android·python·pdf
m0_748230448 小时前
【MySQL】数据库开发技术:内外连接与表的索引穿透深度解析
android·mysql·数据库开发
marui19828 小时前
hadoop sql 执行log
android·ide·android studio
liangmou21219 小时前
解释小部分分WPI函数(由贪吃蛇游戏拓展)
android·游戏·c#
你听得到119 小时前
《Flutter性能优化全攻略:从首屏渲染到性能监测,附案例代码详解》
android·flutter