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

相关推荐
Estar.Lee44 分钟前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯1 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey3 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!4 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟5 小时前
Android音频采集
android·音视频
小白也想学C6 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程6 小时前
初级数据结构——树
android·java·数据结构
闲暇部落9 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX11 小时前
Android 分区相关介绍
android
大白要努力!12 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle