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 层的对应关系如下:
Java 示例程序
接下来我们就来写一个演示 AIDL 数据类型的 Java 示例程序:
首先我们在 device/jelly/rice14/
目录下创建如下的文件与文件夹
bash
BinderJavaTypeDemo
├── Android.bp
└── com
└── yuandaima
├── Client.java
├── HelloService.java
├── IHelloService.aidl
├── Server.java
├── Student.aidl
└── Student.java
其中的 Student.aidl Student.java
是我们自定义的数据类型,在 AIDL 中自定义数据类型需要继承 Parcelable
:
java
//Student.java
package com.yuandaima;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable {
int age;
String name;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.age);
dest.writeString(this.name);
}
public Student() {
}
protected Student(Parcel in) {
this.age = in.readInt();
this.name = in.readString();
}
public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
@Override
public Student createFromParcel(Parcel source) {
return new Student(source);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
在 aidl 中我们只需要简单声明即可:
java
//Student.aidl
package com.yuandaima;
parcelable Student;
IHelloService.aidl 中声明了我们 binder 服务的对外接口:
java
package com.yuandaima;
import com.yuandaima.Student;
interface IHelloService
{
void sayhello();
int sayhello_to(String name);
int printList(in List<String> strs);
int printMap(in Map maps);
int printStudent(in Student student);
}
接着我们编译 aidl 文件:
bash
# 源码目录下
source build/envsetup.sh
# 选择合适的 product
lunch
# 进入项目目录下:
cd com/yuandaima
# -I 用于指定我们的在哪里查找 import
aidl -I . IHelloService.aidl
编译后,就会生成对应的 Java 源文件 IHelloService.java
。
接着编写服务端类 HelloService
:
java
package com.yuandaima;
import android.os.RemoteException;
import android.util.Log;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class HelloService extends IHelloService.Stub {
private static final String TAG = "HelloType";
private int cnt1 = 0;
private int cnt2 = 0;
public void sayhello() throws android.os.RemoteException {
cnt1++;
Log.i(TAG, "sayhello : cnt = " + cnt1);
}
public int sayhello_to(java.lang.String name) throws android.os.RemoteException {
cnt2++;
Log.i(TAG, "sayhello_to " + name + " : cnt = " + cnt2);
return cnt2;
}
public int printList(List<String> strs) throws android.os.RemoteException {
for (int i = 0; i < strs.size(); i++) {
Log.i(TAG, strs.get(i));
}
return 1;
}
@Override
public int printMap(Map maps) throws RemoteException {
Iterator entries = maps.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
Log.i(TAG, "Key = " + key + ", Value = " + value);
}
return 1;
}
@Override
public int printStudent(Student student) throws RemoteException {
Log.d(TAG, "Student name " + student.name + "Student age" + student.age);
return 1;
}
}
接着完成服务端程序 Server.java:
java
package com.yuandaima;
import android.util.Log;
import android.os.ServiceManager;
public class Server {
private static final String TAG = "BinderServer";
public static void main(String args[]) {
/* add Service */
Log.i(TAG, "add hello service");
ServiceManager.addService("hello", new HelloService());
//app_process 启动时,会启动 binder 线程用于获取和解析 binder 消息,应用程序无需关心
while (true) {
try {
Thread.sleep(100);
} catch (Exception e){}
}
}
}
接着完成客户端程序 Client.java
:
java
package com.yuandaima;
import android.util.Log;
import android.os.ServiceManager;
import android.os.RemoteException;
import java.util.ArrayList;
import android.os.IBinder;
import java.util.HashMap;
import java.util.List;
public class Client {
private static final String TAG = "BinderClient";
public static void main(String args[])
{
/* 1. getService */
IBinder binder = ServiceManager.getService("hello");
if (binder == null)
{
Log.i(TAG, "can not get hello service");
return;
}
IHelloService svr = IHelloService.Stub.asInterface(binder);
try {
svr.sayhello();
Log.i(TAG, "call sayhello");
} catch (Exception e) {
}
try {
int cnt = svr.sayhello_to("hello");
Log.i(TAG, "call sayhello_to " + " : cnt = " + cnt);
} catch (Exception e) {
System.out.println("call sayhello_to , err :"+e);
Log.i(TAG, "call sayhello_to , err : "+e);
}
try {
List list = new ArrayList<String>();
list.add("hello ");
list.add("binder");
svr.printList(list);
Log.i(TAG, "call printlist");
} catch (Exception e) {
}
try {
HashMap map = new HashMap();
map.put("Hello", "Map");
svr.printMap(map);
Log.i(TAG, "call printmap");
} catch (Exception e) {
}
try {
Student student = new Student();
svr.printStudent(student);
Log.i(TAG, "call printStudent");
} catch (Exception e) {
}
}
}
最后编译测试:
bash
# 项目目录下执行单编
mm
# 回到系统源码目录
adb push out/target/product/rice14/system/framework/BinderTypeClient.jar /data/local/tmp
adb push out/target/product/rice14/system/framework/BinderTypeServer.jar /data/local/tmp
# 进入模拟器 shell 环境
adb shell
cd /data/local/tmp
export CLASSPATH=/data/local/tmp/BinderTypeClient.jar:/data/local/tmp/BinderTypeServer.jar
app_process /data/local/tmp com.yuandaima.Server &
app_process /data/local/tmp com.yuandaima.Client
接着查看 Log:
bash
08-06 17:43:55.343 10625 10635 I HelloType: sayhello : cnt = 1
08-06 17:43:55.343 10625 10635 I HelloType: sayhello_to hello : cnt = 1
08-06 17:43:55.344 10625 10635 I HelloType: hello
08-06 17:43:55.344 10625 10635 I HelloType: binder
08-06 17:50:57.682 11488 11498 I HelloType: sayhello : cnt = 1
08-06 17:50:57.683 11488 11498 I HelloType: sayhello_to hello : cnt = 1
08-06 17:50:57.683 11488 11498 I HelloType: hello
08-06 17:50:57.683 11488 11498 I HelloType: binder
08-06 17:50:57.683 11488 11498 I HelloType: Key = Hello, Value = Map
08-06 17:50:57.683 11488 11498 D HelloType: Student name nullStudent age0
参考资料
- Android 接口定义语言 (AIDL)
- Generating C++ Binder Interfaces with aidl-cpp
- AIDL interface between Java and C++
关于
我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。
如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。