对于AIDL的学习,这些也只能说是我在学习中的理解,有理解不到位或者错的地方也欢迎指正。

1.AIDL的简单介绍

AIDL的目的就是实现进程之间的通信,尤其是在涉及多进程并发情况下的进程间通信。可以将aidl理解为两个进程之间的桥梁,并制定规则,使其传输特定数据。

1.AIDL支持的数据类型有:

基本数据类型(int、long、char、boolean、double),定向 tag 默认且只能是 instring和charSequence,定向 tag 默认且只能是 inlist:只支持arraylist,以及里面的所有元素必须被aidl支持map:只支持hashmapparcelable:所有实现parcelable接口的对象aidl:所有aidl接口本身也可以在aidl文件中使用

需要注意的是:在aidl中声明其他数据类型时,需要在前面加in(输入型参数)、out(输出型参数)、inout(输入输出型参数)

需要注意的是:除aidl所支持的数据类型外,如果要使用则必须导包,就算目标文件与当前文件在同一包下。

比如: 编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.xxx.aidldemo 包下 ,现在要在 .aidl 文件里使用 Book 对象,那么就必须在 .aidl 文件里面写上 import com.xxx.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。

2.定向tag:

表示在跨进程通信中数据的流向(流向是针对客户端的对象而言) 

in(输入型参数):客户端——>服务器

out(输出型参数):服务器——>客户端

inout(输入输出型参数):双向流通.

3.如果不包含非默认支持的数据类型,只需要编写一个AIDL文件,如果包含,通常需要写 n+1 个AIDL文件( n 为非默认支持的数据类型的种类数)

4.非默认支持数据类型的序列化操作

        1. 使数据类实现Parcelable接口

例:数据从客户端到服务端,可以在客户端对这个对象进行序列化(通常为实现Parcelable接口),将其中数据转化为序列化流,并传输到服务端内存中,再在服务端对这个数据进行反序列化操作,从而还原数据。

        2. 建立一个类,书写其成员变量,建立getter和setter并添加一个无参构造。令这个类implements Parcelable 。

需要注意的是:默认生成的模板类对象只支持为in的定向tag。因为默认生成的类中只有writeToParcel() 方法,如果要实现为 out 或者 inout 的定向 tag 的话,需要实现readFromParcel() 方法。

5.书写AIDL文件 

新建AIDL文件,AIDL文件大致分为两类:

一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型。

// Book.aidl

//用于引入了一个序列化对象Book,供其他的AIDL文件使用

//注意:Book.aidl与Book.java的包名应当是一样的

package com.example.ipcclient;

//注意parcelable是小写

parcelable Book;

一类是用来定义方法接口,以供系统完成跨进程通信。

可理解为通信接口文件,需服务器端及客户端各一个定义,需内容相同。

// BookManager.aidl

package com.example.ipcclient;

//导入所需要使用的非默认支持数据类型的包

import com.lypeer.ipcclient.Book;

interface BookManager {

//所有的返回值前都不需要加任何东西,不管是什么数据类型

List getBooks();

Book getBook();

int getBookCount();

//传参时除了Java基本类型以及String,CharSequence之外的类型

//都需要在前面加上定向tag,具体加什么按需而定

void setBookPrice(in Book book , int price)

void setBookName(in Book book , String name)

void addBookIn(in Book book);

void addBookOut(out Book book);

void addBookInout(inout Book book);

}

 

2.AIDL传递简单数据

1.服务端

        a.创建AIDL文件 

        IPerson.aidl

// IPerson.aidl

package com.example.aidlserver;

import com.example.aidlserver.AIDLService;

interface IPerson {

String queryPerson(int num);

}

创建完后进行build,生成IPerson.java文件

         b.自定义Service类

                i.继承Service类,定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口和IBinder接口。

                ii.实例化自定义的Stub类,重写onBind方法,返回binder对象。

        AIDLService.java

package com.example.aidlserver;

public class AIDLService extends Service {

//实例化自定义的Stub类

private IBinder binder = new PersonQueryBinder();

private String[] names = {"第一个","第二个","第三个"};

private String query(int num)

{

if(num > 0 && num < 6){

return names[num - 1];

}

return null;

}

@Override

public IBinder onBind(Intent intent) {

//重写onBind方法,返回binder对象。

// return new PersonQueryBinder();

return binder;

}

//定义一个PersonQueryBinder类用来继承IPerson.Stub类,实现了IPerson接口。

private final class PersonQueryBinder extends IPerson.Stub {

public String queryPerson(int num) throws RemoteException {

return query(num);

}

}

}

        c.在AndroidManifest.xml文件中注册Service

android:exported="true">

2.客户端

需要将服务端的aidl文件直接复制过来(包括其中的包),客户端的内容直接在Activity中完成即可。

流程如下:

        a.自定义的PersonConnection类实现ServiceConnection接口

        b.以PersonConnection对象作为参数,调用bindService绑定远程Service

bindService(service,conn,BIND_AUTO_CREATE);

        c.绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法

                返回的IBinder对象,只能返回onBind()方法所返回的代理对象,所需处理如下:

iPerson = IPerson.Stub.asInterface(service);

代码:MainActivity.java

package com.example.aidlclient;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private EditText edit_num;

private Button btn_query;

private TextView txt_name;

private IPerson iPerson;

private PersonConnection conn = new PersonConnection();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//控件绑定

edit_num = findViewById(R.id.edit_num);

btn_query = findViewById(R.id.btn_query);

txt_name = findViewById(R.id.txt_name);

//绑定远程Service

Intent service = new Intent("LXY_aidl");

service.setPackage("com.example.aidlserver");

//以PersonConnection对象作为参数,调用bindService绑定远程Service

bindService(service, conn, BIND_AUTO_CREATE);

btn_query.setOnClickListener(this);

}

@Override

public void onClick(View v) {

String number = edit_num.getText().toString();

int num = Integer.valueOf(number);

try {

txt_name.setText(iPerson.queryPerson(num));

} catch (RemoteException e) {

e.printStackTrace();

}

edit_num.setText("");

}

//自定义的PersonConnection类实现ServiceConnection接口

private final class PersonConnection implements ServiceConnection {

public void onServiceConnected(ComponentName name, IBinder service) {

iPerson = IPerson.Stub.asInterface(service);

}

public void onServiceDisconnected(ComponentName name) {

iPerson = null;

}

}

}

3.启动步骤及可能出现的bug

        启动步骤为:先启动服务端,再启动客户端

这个地方可能出现的bug如下: 

Android AIDL客户端调用服务端方法报错空指针,即找不到那个方法。

报错为:java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.example.aidlserver.ILanguage.queryLanguage(int)' on a null object reference

 解决方法:

        1.在客户端清单文件中配置服务端service的包路径

        2.服务端的onBind方法中,返回值不可为空,返回的值为binder对象;或自定义的继承了IPerson.Stub类的类的new 方法。

服务端和客户端AIDL文件所在包路径不一致,报错异常为: 

java.lang.SecurityException: Binder invocation to an incorrect interface

解决方法:修改服务端service对应的完整包名,客户端应与服务端的一致。

服务端:

 客户端:

3.AIDL传递复杂数据

1.基础操作:

        1.将writeToParcel和readFromPacel方法写入方法,将对象写入到包裹(parcel)中。

需要注意的是:写入顺序与读取顺序需要相同

        2.在该类中添加一个名为CREATOR的static final属性 改属性需要实现:android.os.Parcelable.Creator接口

        3.从接口中写两个方法:

        a.createFromParcel(Parcel source)方法:实现从source创建出JavaBean实例

        b.newArray(int size):创建一个数组。

        4.定向tag

2.服务端

代码:Step1:创建aidl文件实现接口.

1.Person.aidl

// Person.aidl

package com.example.aidl_p;

parcelable Person;

2. Salary.aidl

// Salary.aidl

package com.example.aidl_p;

parcelable Salary;

代码:Step2:分别建立类,需实现Parcelable接口,重写对应的方法

1.Person.java

package com.example.aidl_p;

public class Person implements Parcelable {

private Integer id;

private String name;

public Person(Integer id,String name){

this.id = id;

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

protected Person(Parcel in) {

}

//写入数据到Parcel中的方法

@Override

public void writeToParcel(Parcel dest, int flags) {

//把对象所包含的数据写入到parcel中

dest.writeInt(id);

dest.writeString(name);

}

@Override

public int describeContents() {

return 0;

}

//必须提供一个名为CREATOR的static final属性 该属性需要实现

//android.os.Parcelable.Creator接口

public static final Creator CREATOR = new Creator() {

//从Parcel中读取数据,返回Person对象

@Override

public Person createFromParcel(Parcel in) {

return new Person(in.readInt(),in.readString());

}

@Override

public Person[] newArray(int size) {

return new Person[size];

}

};

//因为集合取出元素的时候是根据Person对象来取得

//需要重写hashCode()和equals()方法

@Override

public int hashCode() {

final int prime = 31;

int result = 1;

result = prime * result +((name == null)? 0 : name.hashCode());

return result;

}

//hashCode 自动生成

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Person person = (Person) o;

return Objects.equals(id, person.id) &&

Objects.equals(name, person.name);

}

}

2.Salary.java 

package com.example.aidl_p;

public class Salary implements Parcelable {

private String type;

private Integer salary;

public Integer getSalary() {

return salary;

}

public void setSalary(Integer salary) {

this.salary = salary;

}

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

protected Salary(Parcel in) {

}

public Salary(String type,Integer salary){

this.type = type;

this.salary = salary;

}

@Override

public int describeContents() {

return 0;

}

public static final Creator CREATOR = new Creator() {

//从Parcel中读取数据,返回Person对象

@Override

public Salary createFromParcel(Parcel in) {

return new Salary(in.readString(),in.readInt());

}

@Override

public Salary[] newArray(int size) {

return new Salary[size];

}

};

@Override

public void writeToParcel(Parcel parcel, int i) {

parcel.writeString(type);

parcel.writeInt(salary);

}

public String toString(){

return "工作:" + type + " 薪水: " + salary;

}

}

3.ISalary.aidl  获取工资信息的方法

// ISalary.aidl

package com.example.aidl_p;

//无论在不在同一个包中都需要导包

import com.example.aidl_p.Salary;

import com.example.aidl_p.Person;

interface ISalary {

//定义一个Person对象作为传入参数

//接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in

//获取工资信息

Salary getMsg(in Person owner);

}

4.AidlService.java 

核心Service: 定义一个SalaryBinder类继承Stub,从而实现ISalary和IBinder接口;定义一个存储信息的Map集合, 重新onBind方法,返回SalaryBinder类的对象实例

5.注册Service:

android:enabled="true"

android:exported="true">

3.客户端

代码:Step1:将服务端的aidl复制过来。

代码:Step2:定义一个ServciceConnection对象,重写对应方法,和普通数据的类似,接着bindService,然后再Button的点击事件中获取Salary对象并显示。

MainActivity.java

package com.example.aidl_kehu;

public class MainActivity extends AppCompatActivity {

private ISalary salaryService;

private Button btnquery;

private EditText editname;

private TextView textshow;

private ServiceConnection connection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

//返回的是代理对象,要调用这个方法

salaryService = ISalary.Stub.asInterface(iBinder); }

@Override

public void onServiceDisconnected(ComponentName componentName) {

salaryService = null;

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btnquery = findViewById(R.id.btnquery);

editname = findViewById(R.id.editname);

textshow = findViewById(R.id.textshow);

ActionBar actionBar = getSupportActionBar();

if(actionBar != null){

actionBar.hide();

}

Intent service = new Intent("android.intent.action.AIDLService");

service.setPackage("com.example.aidl_p");

bindService(service,connection, BIND_AUTO_CREATE);

btnquery.setOnClickListener(new View.OnClickListener() {

@SuppressLint("SetTextI18n")

@Override

public void onClick(View view) {

try {

String name = editname.getText().toString();

Salary salary = salaryService.getMsg(new Person(1,name));

textshow.setText(name+salary.toString());

} catch (RemoteException e) {

e.printStackTrace();

}

}

});

}

@Override

protected void onDestroy() {

super.onDestroy();

this.unbindService(connection);

}

}

4.AIDL总结 

1.服务端

        a.创建aidl,创建同名类,在aidl中写parcelable接口。并创建aidl实现接口方法,且需要导包。

        b. 在类中生成set、get方法,创建对象,writeToParcel中把对象写进去,在createFromParcel中返回对象,创建toString() 方法用于输出。

        c.在Service中实现功能

2.客户端

        a.复制服务端的aidl和类,在MainActivity中实现功能

参考阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。