AIDL 数据类型详解之 Java 篇

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

参考资料

关于

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

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

相关推荐
阿巴斯甜31 分钟前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95272 小时前
Andorid Google 登录接入文档
android
黄林晴3 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab16 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿19 小时前
Android MediaPlayer 笔记
android
Jony_19 小时前
Android 启动优化方案
android
阿巴斯甜19 小时前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇19 小时前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android