Android AIDL总结

176 阅读3分钟

AIDL是Android接口定义语言,底层实现是通过Binder实现进程间的通信。

支持数据类型

  • 八种基本数据类型
  • String,Charsequence
  • List
  • Map
  • parcelable
  • Bundle(设置classloader)

多线程处理

  1. 线程安全:AIDL的接口实现必须是线程安全的,因为不同进程的多线程可能同时访问AIDL接口,需要加锁进行操作。
  2. 不要阻塞主线程:不要在AIDL接口中执行耗时操作,因为可能会阻塞调用方的主线程。如果需要执行耗时操作,应该在另一个线程中执行,通过回调发送给调用方。
  3. 处理DeadObjectException:当远程进程死亡时,任何对该进程的AIDL调用都会抛出异常,需要捕获异常重新绑定服务。

自定义类型传递

定义Message类,并且实现parcelable序列化,创建Message.aidl文件,在使用的aidl文件中需要手动导入。客户端和服务端相同文件并且路径要一致。

定向Tag以及oneway

in:数据只能从客户端流向服务端,服务端修改数据不会同步客户端

out:数据只能从服务端流向客户端,客户端传到服务端是空对象,服务端修改这个对象,会同步给客户端

inout:数据双向流动,客户端给服务端是完整对象,服务端改变这个对象也会同步客户端

oneway:主要有两个特性:异步调用和串行化处理。异步调用是指应用向 binder 驱动发送数据后不需要挂起线程等待 binder 驱动的回复,而是直接结束。像一些系统服务调用应用进程的时候就会使用 oneway,比如 AMS 调用应用进程启动 Activity,这样就算应用进程中做了耗时的任务,也不会阻塞系统服务的运行。 串行化处理是指对于一个服务端的 AIDL 接口而言,所有的 oneway 方法不会同时执行,binder 驱动会将他们串行化处理,排队一个一个调用。

RemoteCallbackList

beginBroadcast 开始遍历

getBroadcastItem 获取listener

finishBroadcast 结束遍历

register 注册listener

unregister 解除注册listener

帮我们处理Binder的linkToDeath和unlinkToDeath方法,传入DeathRecipient对象。这样可以帮我们避免了两个进程调用过程发生意外crash,致使 回调 失败或者进程crash的问题。

注册和解除注册的时候传递的listener不是一个对象,因为是进程间传输,服务端把客户端传递过来的对象重新转换为新的对象。ArrayMap<IBinder, Callback> 但是listener对应的Binder对象是相同。

Proxy客户端调用

//客户端获取proxy
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder)

public static com.hugl.testaidlproject.IMyAidlInterface asInterface(android.os.IBinder obj)
{
  if ((obj==null)) {
    return null;
  }
  //判断同一进程
  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  if (((iin!=null)&&(iin instanceof com.hugl.testaidlproject.IMyAidlInterface))) {
    return ((com.hugl.testaidlproject.IMyAidlInterface)iin);
  }
  //返回proxy
  return new com.hugl.testaidlproject.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public java.util.List<com.hugl.testaidlproject.Messages> getMessagesList() throws android.os.RemoteException
{
  //客户端传递数据的parcel  
  android.os.Parcel _data = android.os.Parcel.obtain();
  //服务端返回的数据parcel
  android.os.Parcel _reply = android.os.Parcel.obtain();
  //服务端返回的数据
  java.util.List<com.hugl.testaidlproject.Messages> _result;
  try {
    _data.writeInterfaceToken(DESCRIPTOR);
    //调用Binder,然后stub的ontransact方法数据写入_reply中
    boolean _status = mRemote.transact(Stub.TRANSACTION_getMessagesList, _data, _reply, 0);
    _reply.readException();
    _result = _reply.createTypedArrayList(com.hugl.testaidlproject.Messages.CREATOR);
  }
  finally {
    _reply.recycle();
    _data.recycle();
  }
  return _result;
}

整体流程

Client端:MainActivity;

Server端:Service;

Service的对外aidl接口如上面的案例所示,

MainActivity已经通过BindService拿到了Service的IBinder对象。

aidl通信的基本步骤如下:

  1. Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
  2. 通过Proxy来同步调用IPC方法(testFunction),同时通过Parcel将参数传给Binder,最终触发Binder的transact方法。
  3. Binder的transact方法最终会触发到Server上Stub的onTransact方法。
  4. Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回。
  5. 请注意:Client的Ipc方法中,执行Binder的transact时,是阻塞等待的,一直到Server逻辑执行结束后才会继续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。
  6. 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。