复杂数据类型
除了int、long、boolean 、float 、double 、String 之外,还可以使用继承Parcelable的复杂数据类型,及它们的集合(List)。接下来直接看下复杂数据类型的创建和使用,以一个People为例:
public class People {
String mName;
int mAge;
}
安装android-parcelable-intellij-plugin插件,可以一步自动生成parcelable对象。
不过在最新的android studio中遇到问题无法安装该插件,后来在issue中找到支持的插件
安装好插件后,光标选在类中,右键选择Generate → parcelable
最终生成一个parcelable的对象
public class People implements Parcelable {
public String mName;
public int mAge;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mName);
dest.writeInt(this.mAge);
}
public People() {
}
protected People(Parcel in) {
this.mName = in.readString();
this.mAge = in.readInt();
}
public static final Creator CREATOR = new Creator() {
@Override
public People createFromParcel(Parcel source) {
return new People(source);
}
@Override
public People[] newArray(int size) {
return new People[size];
}
};
}
接下来,需要在和该People的java文件夹同路径下,在aidl中创建一个People.aidl,内容如下
package com.xfz.aidlapplication.model;
parcelable People;
这样就可以使用该People类型了。
复杂数据类型的使用
修改aidl文件,再写一个方法来使用People类型,需要注意的是aidl中不支持重载。
新写的方法为getAge,具体如下:
- 首先需要手动import进来People
- 使用People时,前面会有in的标记(in的标记下面会介绍)
package com.xfz.aidlapplication;
import com.xfz.aidlapplication.model.People;
interface IMyAidlInterface {
String getName(int age);
int getAge(in People people);
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
修改MyBinder
class MyBinder extends IMyAidlInterface.Stub {
@Override
public String getName(int age) throws RemoteException {
return age > 30 ? "老王" : "小王";
}
@Override
public int getAge(People people) throws RemoteException {
return people.mAge;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString)
throws RemoteException {
}
}
同样将People.java和相应aidl同步到客户端进程中,这样,在客户端进程就可以使用
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在服务连接成功后进行调用
IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
People people = new People();
people.mAge = 35;
people.mName = "小王";
Log.e("xfz", "age = " + iMyAidlInterface.getAge(people) + " name = " + people.mName);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
AIDL定向tag:in、out、inout
首先需要知道,所有的非基本类型的参数都需要一个定向tag,用于确定数据流向 。而基本类型参数的定向tag是in,并且只能是in。
数据流向:
- in 表示数据只能由客户端流向服务端
- out 表示数据只能由服务端流向客户端
- inout 表示数据可在服务端与客户端之间双向流通
再具体点说:数据流向是针对在客户端中的那个传入方法的对象而言的。
- in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;
- out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;
- inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
对上面MyBinder进行改造后来验证,getAge中对name进行赋值:
class MyBinder extends IMyAidlInterface.Stub {
@Override
public String getName(int age) throws RemoteException {
return age > 30 ? "老王" : "小王";
}
@Override
public int getAge(People people) throws RemoteException {
people.mName = getName(people.mAge);
return people.mAge;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString)
throws RemoteException {
}
}
如果People还是in,则输出还是小王,因为服务端对传参的修改不会影响客户端,
需要把People改成inout,这样输出就会变成老王。
interface IMyAidlInterface {
……
int getAge(inout People people);
……
}
需要注意,改成inout后会发现找不到readFromParcel方法,需要自己加上
public class People implements Parcelable {
……
protected People(Parcel in) {
this.mName = in.readString();
this.mAge = in.readInt();
}
public void readFromParcel(Parcel in) {
this.mName = in.readString();
this.mAge = in.readInt();
}
……
}
AIDL中的oneway 关键字
oneway 主要有两个特性:异步调用和串行化处理。
- 异步调用是指应用向 binder 驱动发送数据后不需要挂起线程等待 binder 驱动的回复,而是直接结束。像一些系统服务调用应用进程的时候就会使用 oneway,比如 AMS 调用应用进程启动 Activity,这样就算应用进程中做了耗时的任务,也不会阻塞系统服务的运行。
- 串行化处理是指对于一个服务端的 AIDL 接口而言,所有的 oneway 方法不会同时执行,binder 驱动会将他们串行化处理,排队一个一个调用。
oneway可以用来修饰interface,这样interface内所有的方法都隐式地带上oneway。
oneway可以修饰interface里的各个方法,被oneway修饰的方法不可以有返回值,也不可以带out或inout的参数。
使用oneway进行异步回调
这里再顺便结合使用上面说的List,首先新增一个用于回调的接口,ICallbackInterface.aidl
interface ICallbackInterface {
oneway void callback(int totalName);
}
再IMyAidlInterface中新增个oneway方法 getTotalAgeAsync
需要手动import进ICallbackInterface和List类路径
package com.xfz.aidlapplication;
import com.xfz.aidlapplication.model.People;
import com.xfz.aidlapplication.ICallbackInterface;
import java.util.List;
interface IMyAidlInterface {
String getName(int age);
int getAge(inout People people);
oneway void getTotalAgeAsync(in List peopleList, ICallbackInterface callback);
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
然后MyBinder中进行实现
class MyBinder extends IMyAidlInterface.Stub {
……
@Override
public void getTotalAgeAsync(List peopleList, ICallbackInterface callback) throws RemoteException {
if (peopleList == null || peopleList.size() == 0 || callback == null) {
return;
}
//这里也可以扔到异步线程中sleep几秒后再处理,也不会阻塞客户端进程
int totalAge = 0;
for (People people : peopleList) {
totalAge += people.mAge;
}
callback.callback(totalAge);
}
……
}
将上方修改后的aild文件同步到客户端进程后,进行调用
参考:
你真的理解AIDL中的in,out,inout么?
你真的懂AIDL的oneway嘛
看你简历上写熟悉 AIDL,说一说 oneway 吧