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 可以看出我们的程序指针成功了。
参考资料
- Android 接口定义语言 (AIDL)
- Generating C++ Binder Interfaces with aidl-cpp
- AIDL interface between Java and C++
关于
我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。
如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。