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

AIDL进程间通信详解

2016-05-12 11:09 477 查看
什么是AIDL?

IDL是一种内部进程间通信的描述语言,而AIDL,则是Android中内部进程间通信的描述语言,Android接口定义语言。(Android Interface Definition Language)

AIDL有什么作用呢?

进程间通信,也就是说在Android手机中,每一个应用程序都拥有自己的虚拟机,他们是没有办法直接进行数据之间的交换的,我们需要通过某种方式才能实现进程间的通信,那么,Android中我们就可以通过AIDL来实现进程间通信,AIDL通过系统底层来实现进程间通信,且基于Service 。

AIDL的特点是什么?

谷歌官方文档中说:

使用Binder的情况是:有IPC(进程间通信),没有多线程,多个应用程序;

使用Messenger情况是:只有IPC,没有多线程;

使用AIDL情况是:有IPC,多线程,且多个应用程序;只有你允许客户端从不同的应用程序,为了进程间的通信而去访问你的Service,以及想在你的Service处理多线程才会使用。

AIDL基本语法:

1.语法和Java的接口类似

2.AIDL只支持方法,不能定义静态成员

3.AIDL运行方法有任何类型的参数和返回值

4.除默认类型外,均需要导包

AIDL如何使用?

1.创建你的 .aidl 文件(eclipse中自动编译,Android Studio中序需手动编译)

//包名
package com.test.aidl;
//AIDL语言类似于java接口写法
interface IRemoteService{
int add(int numA,int numB);
}

创建这个AIDL文件后,如果没有错误,eclipse会在工程的gen目录下面生成对应名字的 java文件,这个文件则会生成 IRemoteService.java文件,后面我们会详细解析一下这个文件。

2.服务端实现这个接口,也就是实现 add(int numA, int numB) 这个方法

//服务端对信息的共享
public class RemoteService extends Service{

/**
* 当客户端绑定该服务时会执行
*/
@Override
public IBinder onBind(Intent intent) {

return iBinder;
}

private IBinder iBinder = new IRemoteService.Stub() {

@Override
public int add(int numA, int numB) throws RemoteException {

return numA + numB;
}
};
}


3.共享这个接口给客户端

在客户端的这个程序中,加入同样的 .aidl 文件

首先客户端显示绑定Intent

//获取到服务端
Intent intent = new Intent();
//显示Intent启动,绑定服务
intent.setComponent(new ComponentName
("package com.test.aidl","package com.test.aidl.IRemoteService"));
bindService(intent , conn , Context.BIND_AUTO_CREATE);
在ServiceConnection中,获取远程服务

IRemoteService iRemoteService;
private ServiceConnection conn=new ServiceConnection() {

//绑定上服务
@Override
public void onServiceDisconnected(ComponentName name) {
iRemoteService = null;
}
//服务断开的时候
@Override
public void onServiceConnected(ComponentName name, IBinder service) {

//拿到远程服务,是一个Proxy代理类
iRemoteService = IRemoteService.Stub.asInterface(service);

}
};
通过 iRemoteService 就可以调用方法了

try{
//注意抛出远程异常
int res = iRemoteService.add(num1,num2);
}catch (RemoteException e){
e.printStackTrace();
}
整个过程就是AIDL的进程间通信了,记住,在两个进程(程序)中都要放入相同的 .aidl 文件。

AIDl 默认支持的数据类型:

1.基本数据类型(short类型不支持,在 writeToParcel 序列化时中没有 writeShort 方法)

2.String 、 CharSequence

3. List 、 Map

4.序列化后的对象

分析 ,aidl 文件编译器生成同名的 .java 文件,我们需要明白其内部机制,它是如何进行进程间通信的?

这个是生成 java文件的大致结构,下面我们会依次解释里面的方法。

package com.test.aidl;
//AIDL语言类似于java接口写法
//自动生成文件,不能修改
public interface IRemoteService extends android.os.IInterface
{

//本地端生成一个抽象存根类
public static abstract class Stub extends android.os.Binder implements com.test.aidl.IRemoteService
{

//静态描述符,用于标识整个 .aidl 文件
private static final java.lang.String DESCRIPTOR = "com.test.aidl.IRemoteService";

//在构造函数中绑定类名(静态描述符)
public Stub()

//客户端调用此方法,返回一个远程服务的代理类
public static com.test.aidl.IRemoteService asInterface(android.os.IBinder obj)

@Override
public android.os.IBinder asBinder()

//在代理类Proxy调用Transact方法只会调用 onTransact方法
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

//代理类,运用代理模式。调用transact方法,将序列化数据通过系统底层传输到Stub类,调用onTransact方法
private static class Proxy implements com.test.aidl.IRemoteService

// 给 .aidl 文件中的远程调用方法,指定一个code,用于标识,在onTransact方法中去判断
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

//接口中的方法
public int add(int numA, int numB) throws android.os.RemoteException;
}
相信很清楚了吧。

生成与 .aidl 文件同名的接口,其中包括(Stub类,add方法)

在Stub类中有几个很重要的方法或类:asInterface ; onTransact ; Proxy(代理类)

我们先来看看 asInterface 方法:

//客户端调用此方法,返回一个远程服务的代理类
public static com.test.aidl.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.test.aidl.IRemoteService))) {
return ((com.test.aidl.IRemoteService)iin);
}
return new com.test.aidl.IRemoteService.Stub.Proxy(obj);
}
这个方法是在客户端调用,此时的 iRemoteService是一个代理类

//拿到远程服务,是一个Proxy代理类
iRemoteService = IRemoteService.Stub.asInterface(service);


再来看看代理类里面发生了什么事?
//代理类,运用代理模式。调用transact方法,将序列化数据通过系统底层传输到Stub类,调用onTransact方法
private static class Proxy implements com.test.aidl.IRemoteService
{
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 int add(int numA, int numB) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//代理类中将数据序列化
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(numA);
_data.writeInt(numB);
//通过transact方法将数据通过系统底层发送出去
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
最重要的就是在代理类中,我们调用了 transact 方法,把我们的数据从客户端传输出去。transact 是Binder类的一个方法,我们就去Binder的源码之中看看,transact是怎么实现的吧?

public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
//调用onTransact方法,将数据传入
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}


Ok,Ok,在 transact 中,我们调用了 onTransact 方法,我们再来一步一步看看onTransact 方法吧。

//在代理类Proxy调用Transact方法只会调用 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_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
我们就在 onTransact 中处理客户端传来的数据,服务端通过 code 来确定客户端所请求的目标方法,从 data中取出目标方法所需参数,执行完目标方法后,向 reply 之中写入返回值,这就是 onTransact 方法执行过程,如果此方法返回 false 表示客户端请求会失败。

这是整个过程的结构图:



如果有什么错误,请大家多多提出,谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: