简介
AIDL(Android接口定义语言)定义客户端与服务使用进程间通信(IPC)进行相互通信时都认可的编程接口。
使用场景
只有允许不同应用的客户端用IPC的方式访问服务,并且想要在服务中处理多线程时,才有必要使用AIDL。如果向执行IPC,但是根本不需要处理多线程,则使用Message类来实现接口。
使用aidl创建绑定服务,需要以下几个步骤:
1.创建.aidl文件。此文件定义带有方法签名的编程接口
2.实现接口
3.向客户端公开接口
创建.aidl文件
.aidl文件主要用于定义编程接口的,定义接口时默认支持的类型有:
- 1.Java编程语言中所有原语类型(int, long, char, boolean等)
- 2.String, CharSequence
- 3.List, Map
- 4.自定义的类:? implementsParcelable
在使用List和Map的时候,其包含的元素也必须是上述所支持的类型。
定义接口时,需要注意以下几点:
- 1.方法可带零个或者多个参数,返回值可以是空值
2.所有非原语参数都要指明数据走向的方向标记,可以是in、out、inout,原语参数默认是in,且不能改变
3.只支持方法,不能公开AIDL中的静态字段
以下是一个aidl文件创建的例子:
package com.aidl;
import com.aidl.Entity;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void inWay(in Entity entity);
void outWay(out Entity entity);
void inOutWay(inout Entity entity);
}
在这里定义了三个接口,并且在接口里使用的是我们自己定义的实体类:Entity,并且分别使用了三个数据走向的方向标志:in、out、inout,这三个具体什么含义,先放在这,后面会进行说明。使用自定义类型时,不管这个类型放在哪个目录下,即使是同一个目录,也要用import导入。下面要说下这个实体类Entity要怎么定义。
针对自定义的类型,要有两个文件:.aidl和.java,.aidl的定义如下:
// Entity.aidl
package com.aidl;
import com.aidl.Entity;
// Declare any non-default types here with import statements
parcelable Entity;
这里用parcelable作为前缀修饰(parcelable是小写),并且import导入真正的java类,其定义为:
package com.aidl;
import android.os.Parcel;
import android.os.Parcelable;
//实现Parcelable接口
public class Entity implements Parcelable{
private String name;
private int size;
public Entity(){}
public Entity (String name, int size) {
this.name = name;
this.size = size;
}
public Entity (Parcel in) {
readFromParcel(in);
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public void setName(String name) {
this.name = name;
}
public void setSize(int size) {
this.size = size;
}
//定义CREATOR
public static final Creator<Entity> CREATOR = new Creator<Entity>() {
@Override
public Entity createFromParcel(Parcel parcel) {
return new Entity(parcel);
}
@Override
public Entity[] newArray(int i) {
return new Entity[i];
}
};
//writeToParcel
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(size);
}
//readFromParcel
public void readFromParcel(Parcel in) {
name = in.readString();
size = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
}
上面标注的几个是必须要实现的。写好之后,build下项目,系统会为.aidl文件生成对应的java文件:
package com.aidl;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.aidl.IMyAidlInterface";
//略去实现
private static class Proxy implements com.aidl.IMyAidlInterface {
//略去实现
}
static final int TRANSACTION_inWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_outWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_inOutWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void inWay(com.aidl.Entity entity) throws android.os.RemoteException;
public void outWay(com.aidl.Entity entity) throws android.os.RemoteException;
public void inOutWay(com.aidl.Entity entity) throws android.os.RemoteException;
}
系统生成的IMyAidlInterface是一个接口,三个方法即为我们在aidl文件中写的三个方法,这三个方法里面是没有in、out、inout的。在IMyAidlInterface中有一个抽象类的实现:stub,stub也是一个Binder,在这个抽象类中又有一个私有的静态类的实现:Proxy,这些是系统为我们生成的东西。下面我们看下stub和Proxy的具体实现:
public static abstract class Stub extends android.os.Binder implements com.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.aidl.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 生成一个代理对象Proxy
*/
public static com.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.IMyAidlInterface))) {
return ((com.aidl.IMyAidlInterface) iin);
}
return new com.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_inWay: {
data.enforceInterface(DESCRIPTOR);
com.aidl.Entity _arg0;
if ((0 != data.readInt())) {
//从data中创建一个Entity,此处data携带这Proxy.inWay中的Entity信息
_arg0 = com.aidl.Entity.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.inWay(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_outWay: {
data.enforceInterface(DESCRIPTOR);
com.aidl.Entity _arg0;
_arg0 = new com.aidl.Entity();
this.outWay(_arg0);
reply.writeNoException();
if ((_arg0 != null)) {
reply.writeInt(1);
//新建一个Entity, 写到reply中
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_inOutWay: {//兼容了in和out
data.enforceInterface(DESCRIPTOR);
com.aidl.Entity _arg0;
if ((0 != data.readInt())) {
_arg0 = com.aidl.Entity.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.inOutWay(_arg0);
reply.writeNoException();
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.aidl.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* inWay的实现:此处数据走向为in
*/
@Override
public void inWay(com.aidl.Entity entity) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((entity != null)) {
_data.writeInt(1);
entity.writeToParcel(_data, 0);//只有write没有read
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_inWay, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
//outWay的实现:此处数据走向为out
@Override
public void outWay(com.aidl.Entity entity) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_outWay, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
entity.readFromParcel(_reply);//只有read没有write
}
} finally {
_reply.recycle();
_data.recycle();
}
}
//inout的实现:此处数据走向为inout
@Override
public void inOutWay(com.aidl.Entity entity) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((entity != null)) {
_data.writeInt(1);
entity.writeToParcel(_data, 0);//写入data
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_inOutWay, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
entity.readFromParcel(_reply);//从reply中读
}
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_inWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_outWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_inOutWay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
Proxy相当于一个代理类,其是IMyAidlInterface的具体实现,针对in、out、inout有不同 的实现策略,Proxy的参数Binder是从stub中传入。
此处解释下in、out、inout的区别:
- in表示数据走向为:客户端->服务端,可理解为数据可以从客户端传到服务端,但是不能从服务端再回传到客户端
这应该怎么理解呢?我们知道,AIDL接口的调用是直接函数的调用,函数调用传入的类型如果是我们自己定义的java类的话,那么在函数里面是可以对这个实例的内容进行修改的,或者举个List的例子:
public void test (List<String> list) {
if (list != null) {
list.add("test");
}
}
如果传入的list不为空的话,执行之后,list的size是会增加1的,即可以对list进行修改,或者理解为数据流向是双向的,可以从外部将数据给test函数,此函数内部对list的修改也可以流入test外部。而in在此处的作用就是,服务端可以完整的接受到客户端传来的数据,但是服务端对客户端传来的数据进行修改之后,客户端的数据依然是不会变的,因为服务端对客户端的修改不会流入客户端,这也是为什么java编程语言中所有的原语类型的数据都默认是in,不能是其他方向。从上面系统为我们生成的java类中也可以看出,当类型是in的时候,只有write,却没有read,即只可以传入,却不能回读。
- out表示数据走向为服务端到客户端,不能从客户端流向服务端
这个怎么理解呢?就是说我从客户端传入的参数,服务端是得不到具体内容的,但是我对这个参数进行修改的同时,客户端却也相应做出了改变。从上面代码中也可以看出,当流向为out的时候,新建了一个Entity,即相当于一个空壳子,但是有read回读操作,在后面也会对其做相应验证,以便加深理解。
- inout表示数据走向为双向的,可以理解为in和out的合体
服务器的实现
服务器要做的事情就是实现一个Service,然后返回一个Binder,这个Binder是Stub的实现,实现了在aidl文件中定义的所有接口,如下是一个实现:
package com.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.aidl.Entity;
import com.aidl.IMyAidlInterface;
/**
* Created by smartisan on 07/03/17.
*/
public class AidlService extends Service{
//Stub的实现
private static final IMyAidlInterface.Stub bind = new IMyAidlInterface.Stub() {
@Override
public void inWay(Entity entity) throws RemoteException {
if (entity != null) {
//这里打印相关信息,冰对entity做相应修改,用于测试客户端是否也做出了修改
Log.d("tservice", "inway: " + entity.getName() + " " + entity.getSize() + " currentthread: " + Thread.currentThread());
entity.setName("back in");
}
}
@Override
public void outWay(Entity entity) throws RemoteException {
if (entity != null) {
//与inWay同理
Log.d("tservice", "outway: " + entity.getName() + " " + entity.getSize() + " currentthread: " + Thread.currentThread());
entity.setName("back out");
}
}
@Override
public void inOutWay(Entity entity) throws RemoteException {
if (entity != null) {
//与inWay同理
Log.d("tservice", "inoutway: " + entity.getName() + " " + entity.getSize() + " currentthread: " + Thread.currentThread());
entity.setName("back in out");
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
//这里要返回Stub的一个实现
return bind;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("tservice","aidlservice create: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("tservice","ondestroy");
}
}
客户端的实现
在服务端建立的.aidl文件和实体类java文件要按目录赋值到客户端。客户端需要做的有:
bind服务端实现的那个Service
实现一个ServiceConnection,然后在onServiceConnected中利用Stub.asInterface((IBinder)service)返回一个实例
利用上述返回的实例传递数据
以下是一个简单的实现:
实现ServiceConnection
private class Conn implements ServiceConnection {
//disconnected在unbindservice的时候不会调用,在service端的service被销毁时调用,例如service重新安装
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("tservice","service: " + service.getClass().getName() + " name: " + name);
//生成实现的实例
tse = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("tservice","service: disconnect" + " name: " + name);
}
}
//发送数据
private void send () {
Entity entity = new Entity("db",10);
Entity entityout = new Entity("dbout",10);
Entity entityinout = new Entity("dbinout",11);
try {
tse.inWay(entity);
tse.outWay(entityout);
tse.inOutWay(entityinout);
Log.d("tservice","inway: " + entity.getName());
Log.d("tservice","outway: " + entityout.getName());
Log.d("tservice","inoutway: " + entityinout.getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
通信结果
通过log去分析最终结果,按上述实现方式运行,发送之后,得到的客户端log为:
03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: inway: db
03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: outway: back out
03-11 22:03:58.592 1459-1459/net.db.com.netdb D/tservice: inoutway: back in out
服务端log为:
03-11 22:03:58.587 421-467/com.lw.myrecyclerview D/tservice: inway: db 10 currentthread: Thread[Binder_3,5,main]
03-11 22:03:58.589 421-504/com.lw.myrecyclerview D/tservice: outway: null 0 currentthread: Thread[Binder_4,5,main]
03-11 22:03:58.591 421-525/com.lw.myrecyclerview D/tservice: inoutway: dbinout 11 currentthread: Thread[Binder_5,5,main]
从上述可以看出: 数据走向in、out、inout三者之间的区别
以上只是对aidl做了一个简单的介绍,其底层的实现原理还有待进一步探讨。