您的位置:首页 > 大数据 > 人工智能

AIDL:进程间通信

2015-12-30 17:08 591 查看
简介

创建aidl文件

服务器的实现

客户端的实现

通信结果

简介

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类的话,那么在函数里面是可以对这个实例的内容进行修改
c86e
的,或者举个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做了一个简单的介绍,其底层的实现原理还有待进一步探讨。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息