AIDL学习及解析《一》

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/uestcyms/article/details/80328534

简介

AIDL:Android Interface Definition Language,android接口定义语言。可以利用它定义客户端与服务端使用进程间通信进行相互通信时都认可的编程接口;Android上,一个进程通常无法访问另一个进程的内存。进程需要将其对象分解成能够识别的原语,并将对象编组成跨越边界的对象。编写执行这一编组操作的代码是一项繁琐的工作。因此Android会使用AIDL。

使用场景

不同应用的客户端使用IPC方式进行访问服务,并且想要在服务中处理多线程;该种情况下需使用AIDL。

如果不需要执行跨越不同应用的并发IPC,应该通过实现一个Binder创建接口;
如果你想执行IPC,但根本不需要处理多线程,则使用Messager类来实现接口(Messager也是基于AIDL进行实现的)。

使用

AIDL的使用基本分为以下几步:
1. 在项目 src/ 目录中加入 .aidl 文件。
2. 声明一个 IBinder 接口实例(基于 AIDL 生成)。
3. 实现 ServiceConnection。
4. 调用 Context.bindService(),以传入您的 ServiceConnection 实现。
5. 在您的 onServiceConnected() 实现中,您将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
6. 调用您在接口上定义的方法。您应该始终捕获 DeadObjectException 异常,它们是在连接中断时引发的;这将是远程方法引发的唯一异常。
7. 如需断开连接,请使用您的接口实例调用 Context.unbindService()。

客户端、服务端、Binder驱动

Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。

服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。

Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。

客户端接口:获得Binder驱动,调用其transact()发送消息至服务器。

这里写图片描述

Aidl接口类图关系

这里写图片描述

oneway

关键字,用于修改远程调用的行为。

  1. 本地调用
    如果oneway用于本地调用,则不会有任何影响,调用仍是同步调用。
  2. 远程调用
    使用该关键字时,远程调用不会阻塞,它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自Binder线程池的常规调用进行接收。

不加oneway的代码:

Proxy类:
private static class Proxy implements demo.sunrise.com.aidldemo.IMyAidlTest
{
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;
}
@Override public void max(int x, int y) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_max, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}

onTransact方法:
@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_max:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.max(_arg0, _arg1);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

加入oneway代码:

Proxy类:
private static class Proxy implements demo.sunrise.com.aidldemo.IMyAidlTest
{
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;
}
@Override public void max(int x, int y) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_max, _data, null, android.os.IBinder.FLAG_ONEWAY);
}
finally {
_data.recycle();
}
}
}

onTransact方法:
@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_max:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
this.max(_arg0, _arg1);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

根据代码发现加入oneway后,客户端接口的proxy代理类的实现有两个区别:

  • reply序列对象为空,不会构造reply的序列化对象。
  • transaction的第四个参数由0改为1(android.os.IBinder.FLAG_ONEWAY)。

in out inout

所有非原语参数都需要指示数据走向的方向标记。可以是 in、out 或 inout(见以下示例)。
原语默认为 in,不能是其他方向。

注意:您应该将方向限定为真正需要的方向,因为编组参数的开销极大。

使用void testTransactList( List list)方法进行测试:
inout代码:

Proxy:
@Override public void testTransactList(java.util.List list) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();// 客户端数据包
android.os.Parcel _reply = android.os.Parcel.obtain();// 服务端数据包
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeList(list);// 客户端数据包组包
mRemote.transact(Stub.TRANSACTION_testTransactList, _data, _reply, 0);//通过binder驱动调用服务端的ontransact
_reply.readException();
java.lang.ClassLoader cl = (java.lang.ClassLoader)this.getClass().getClassLoader();
_reply.readList(list, cl);// 服务端数据包拆包,并修改客户端方法参数
}
finally {
_reply.recycle();
_data.recycle();
}
}

onTransact:
case TRANSACTION_testTransactList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
java.lang.ClassLoader cl = (java.lang.ClassLoader)this.getClass().getClassLoader();
_arg0 = data.readArrayList(cl);// 客户端数据包拆包
this.testTransactList(_arg0);// 执行服务端实现方法
reply.writeNoException();
reply.writeList(_arg0);// 服务端数据包组包
return true;
}

in代码:

Proxy:
@Override public void testTransactList(java.util.List list) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();// 客户端数据包
android.os.Parcel _reply = android.os.Parcel.obtain();// 服务端数据包 
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeList(list);// 将客户端函数参数写入到客户端数据包里,也就是组包
mRemote.transact(Stub.TRANSACTION_testTransactList, _data, _reply, 0);//通过binder驱动调用服务端的ontransact
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

onTransact:
case TRANSACTION_testTransactList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
java.lang.ClassLoader cl = (java.lang.ClassLoader)this.getClass().getClassLoader();
_arg0 = data.readArrayList(cl);// 从客户端数据包中拆包

this.testTransactList(_arg0);
reply.writeNoException();
return true;
}

out代码:

Proxy(客户端的代理):
@Override public void testTransactList(java.util.List list) 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_testTransactList, _data, _reply, 0);//通过binder驱动调用服务端的ontransact
_reply.readException();
java.lang.ClassLoader cl = (java.lang.ClassLoader)this.getClass().getClassLoader();
_reply.readList(list, cl);// 读取服务端,并更改客户端的list
}
finally {
_reply.recycle();
_data.recycle();
}
}

onTransact:
case TRANSACTION_testTransactList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = new java.util.ArrayList();// 新创建一个List对象,并不是从client端的数据中读取,client传入的data也是空的(除了写入了token)
this.testTransactList(_arg0);
reply.writeNoException();
reply.writeList(_arg0);// 将服务端处理后的list,写入到服务端数据
return true;
}
}

可以查看上述3中情况,Proxy(客户端AIDL接口的代理类)和服务端aidl接口的onTransact(最终Binder驱动回调到服务端的方法)的实现区别;

数据类型 客户端 data reply 服务端 data reply
intout 客户端将参数组包至data,并传递给服务端;执行完后,将服务端reply拆包更新到参数中 服务端从客户端传来的data拆包获取数据,然后传入服务端进行执行(服务端接口实现),最后将数据组包到reply
in 客户端将参数组包至data,并传递给服务端,不对reply进行拆包更新参数的动作 服务端从客户端传来的data拆包获取数据,然后传入服务端进行执行(服务端接口实现)
out 不将参数组包至data(“为空”无数据),然后传递给服务端,执行过后将服务端reply拆包更新到参数中 服务端自己新new一个数据对象,然后交由接口实现方法处理,将处理过后的数据,组包至reply

通过代码和表中的总结:inout:进行4次编组;in:进行2次编组;out:进行两次编组;所以我们需要指定参数的数据流向,否则会照成资源的浪费或性能有影响。

in、out、inout是相对于服务端;(流入为in、流出为out,可以参照stream进行理解)

支持的数据类型

  • Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
  • String
  • CharSequence
  • List
    List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
  • Map
    Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map

传输Map类型数据中的小问题

在此记录下使用Map作为传输数据类型的一个小问题:
测试方法:void testTransactMap(xxxMap map);
当参数数据流向为out时,编译出错,错误信息为:error: Map is abstract; cannot be instantiated
错误信息翻译过来就是:Map属于抽象类,不能被初始化
定位代码:上面讲解数据流向的时候,知道out流向时,服务端会自己实例化一个对象并传递接口实现的方法,那么我们看aidl服务端接口的java文件:
代码如下

case TRANSACTION_testTransactMap:
{
data.enforceInterface(DESCRIPTOR);
java.util.Map _arg0;
arg0 = new java.util.Map();//出错代码
this.testTransactMap(_arg0);
reply.writeNoException();
reply.writeMap(_arg0);
return true;
}

由于AIDL的java文件由IDE自动生成,暂且认为是系统的一个bug,也可以说Map的数据类型不支持out的数据流向。

猜你喜欢

转载自blog.csdn.net/uestcyms/article/details/80328534