AIDL:进程间通信
2015-12-30 17:08
591 查看
简介
创建aidl文件
服务器的实现
客户端的实现
通信结果
使用场景
只有允许不同应用的客户端用IPC的方式访问服务,并且想要在服务中处理多线程时,才有必要使用AIDL。如果向执行IPC,但是根本不需要处理多线程,则使用Message类来实现接口。
使用aidl创建绑定服务,需要以下几个步骤:
1.创建.aidl文件。此文件定义带有方法签名的编程接口
2.实现接口
3.向客户端公开接口
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文件创建的例子:
在这里定义了三个接口,并且在接口里使用的是我们自己定义的实体类:Entity,并且分别使用了三个数据走向的方向标志:in、out、inout,这三个具体什么含义,先放在这,后面会进行说明。使用自定义类型时,不管这个类型放在哪个目录下,即使是同一个目录,也要用import导入。下面要说下这个实体类Entity要怎么定义。
针对自定义的类型,要有两个文件:.aidl和.java,.aidl的定义如下:
这里用parcelable作为前缀修饰(parcelable是小写),并且import导入真正的java类,其定义为:
上面标注的几个是必须要实现的。写好之后,build下项目,系统会为.aidl文件生成对应的java文件:
系统生成的IMyAidlInterface是一个接口,三个方法即为我们在aidl文件中写的三个方法,这三个方法里面是没有in、out、inout的。在IMyAidlInterface中有一个抽象类的实现:stub,stub也是一个Binder,在这个抽象类中又有一个私有的静态类的实现:Proxy,这些是系统为我们生成的东西。下面我们看下stub和Proxy的具体实现:
Proxy相当于一个代理类,其是IMyAidlInterface的具体实现,针对in、out、inout有不同 的实现策略,Proxy的参数Binder是从stub中传入。
此处解释下in、out、inout的区别:
in表示数据走向为:客户端->服务端,可理解为数据可以从客户端传到服务端,但是不能从服务端再回传到客户端
这应该怎么理解呢?我们知道,AIDL接口的调用是直接函数的调用,函数调用传入的类型如果是我们自己定义的java类的话,那么在函数里面是可以对这个实例的内容进行修改
c86e
的,或者举个List的例子:
如果传入的list不为空的话,执行之后,list的size是会增加1的,即可以对list进行修改,或者理解为数据流向是双向的,可以从外部将数据给test函数,此函数内部对list的修改也可以流入test外部。而in在此处的作用就是,服务端可以完整的接受到客户端传来的数据,但是服务端对客户端传来的数据进行修改之后,客户端的数据依然是不会变的,因为服务端对客户端的修改不会流入客户端,这也是为什么java编程语言中所有的原语类型的数据都默认是in,不能是其他方向。从上面系统为我们生成的java类中也可以看出,当类型是in的时候,只有write,却没有read,即只可以传入,却不能回读。
out表示数据走向为服务端到客户端,不能从客户端流向服务端
这个怎么理解呢?就是说我从客户端传入的参数,服务端是得不到具体内容的,但是我对这个参数进行修改的同时,客户端却也相应做出了改变。从上面代码中也可以看出,当流向为out的时候,新建了一个Entity,即相当于一个空壳子,但是有read回读操作,在后面也会对其做相应验证,以便加深理解。
inout表示数据走向为双向的,可以理解为in和out的合体
bind服务端实现的那个Service
实现一个ServiceConnection,然后在onServiceConnected中利用Stub.asInterface((IBinder)service)返回一个实例
利用上述返回的实例传递数据
以下是一个简单的实现:
服务端log为:
从上述可以看出: 数据走向in、out、inout三者之间的区别
以上只是对aidl做了一个简单的介绍,其底层的实现原理还有待进一步探讨。
创建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做了一个简单的介绍,其底层的实现原理还有待进一步探讨。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories