经过前面几篇文章相信,你应该已经基本掌握了AIDL的用法,但或许只知其然不知其所以然,AIDL 是如何一步步实现跨进程通信的,当然本质是通过Binder 机制(但并不意味着AIDL就是Binder),但是如何使用Binder呢?请注意这篇文章不会深入Binder,因为Binder 作为Android 的IPC 通信核心机制,要总结起来实在是太多太多知识点了,这篇文章主要是着重于AIDL的设计和实现原理,系列文章:
Android接口定义语言AIDL (Android Interface Definition Language)定义了客户端与服务端均认可的编程接口规范,以便二者使用进程间通信 (IPC) 进行相互通信,AIDL文件会在编译时由SDK 工具生成对应的Java接口文件和实现类文件,然后通过回调方法返回一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法,AIDL的本质是系统提供了一套可快速实现Binder的工具。
官方建议:仅当需要在不同应用的客户端通过 IPC 方式访问服务(在服务中进行多线程处理)时才有必要使用 AIDL。若无需跨越不同应用执行并发 IPC,则应通过实现 Binder 来创建接口;又或者只想执行 IPC,但不需要处理多线程时使用 Messenger 来实现接口。
名称 | 说明 |
---|---|
IBinder | IBinder是一个接口,代表了跨进程通信的能力,Binder对象。 |
IInterface | IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口) |
AIDL接口 | 继承IInterface。 |
Binder | Java 层的 Binder 类,代表的其实就是 Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理;这两个类都继承自 IBinder, 因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder 驱动会自动完成这两个对象的转换。 |
Stub类 | Binder的实现类,负责接收Binder驱动的连接,服务端通过这个类来提供服务。 |
Proxy类 | 服务器的本地代理,负责应用层向底层通信,客户端通过这个类调用服务器的方法。 |
asInterface() | 客户端调用,将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象。如果客户端和服务端位于统一进程,则直接返回Stub对象本身,否则返回系统封装后的Stub.proxy对象 |
asBinder() | 根据当前调用情况返回代理Proxy的Binder对象。 |
onTransact() | 运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。 |
transact() | 运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的onTransact()直到远程请求返回,当前线程才继续执行。 |
在定义好aidl文件之后,我们对项目进行编译,编译时SDK 工具会自动生成aidl文件对应的Java 接口文件,编译成功之后就可以在build 目录下看到对应的Java文件。
不同版本的AS 保存的具体路径有所不同,AS 3.4.x 是在xxx\build\generated\aidl_source_output_dir下。
以一个简单获取湿度的IGetWet.aidl为例:
package com.crazymo.remoteserver;
interface IGetWet {
float getWet();
}
编译后会自动生成IGetWet.java 接口类文件:
// aidl文件存放的包名
package com.crazymo.remoteserver;
//IGetWet aidl文件的名称 继承IInterface
public interface IGetWet extends android.os.IInterface{
/** Default implementation for IGetWet. */
public static class Default implements com.crazymo.remoteserver.IGetWet{
@Override
public float getWet() throws android.os.RemoteException{
return 0.0f;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.crazymo.remoteserver.IGetWet{
private static final java.lang.String DESCRIPTOR = "com.crazymo.remoteserver.IGetWet";
/** Construct the stub at attach it to the interface. */
public Stub(){
//Stub 初始化时,将自己(实际上是将继承类)绑定到 Binder 的 interface 上
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.crazymo.remoteserver.IGetWet interface,
* generating a proxy if needed.
*/
public static com.crazymo.remoteserver.IGetWet asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.crazymo.remoteserver.IGetWet))) {
return ((com.crazymo.remoteserver.IGetWet)iin);
}
return new com.crazymo.remoteserver.IGetWet.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder(){
return this;
}
/**
* 根据code参数来处理,这里面会调用真正的业务实现类
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
java.lang.String descriptor = DESCRIPTOR;
switch (code){
case INTERFACE_TRANSACTION:{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getWet:{
data.enforceInterface(descriptor);
float _result = this.getWet();
reply.writeNoException();
reply.writeFloat(_result);
return true;
}
default:{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.crazymo.remoteserver.IGetWet{
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 float getWet() throws android.os.RemoteException{
//如果有传入参数的话 最终还是序列化后写入_data,而底层响应是写到_reply
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getWet, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getWet();
}
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.crazymo.remoteserver.IGetWet sDefaultImpl;
}
//类似方法的id作用
static final int TRANSACTION_getWet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.crazymo.remoteserver.IGetWet impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.crazymo.remoteserver.IGetWet getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public float getWet() throws android.os.RemoteException;
}
说明:
- aidl文件代表.aidl
- aidl接口代表的是aidl文件生成的对应的Java接口;
- Binder对象并非特指而是泛指Binder 类对象
作为Framework 层提供给应用使用Binder的基础接口,所以如果你要自己去使用Binder通信的话,你的接口类也必须去继承这个接口。
如果你能跟到更深层次,发现它只是一种抽象设计,是为了让应用业务逻辑面向接口编程,而不用过度依赖实现。
简而言之,IInterface 代表的就是跨进程对象具备什么样的能力(定义了哪些通信接口方法),对应着aidl文件定义的方法,用于声明跨进程通信的业务接口方法。
注意:对于通过aidl文件自动生成对应的aidl接口方式来说IInterface是必不可少的,而如果是自己手动实现则不是必要的。
Binder对象/代理对象在服务端和客户端是共享的,在客户端通过Binder对象获取实现了IInterface接口的对象来调用远程服务,Parcelable序列化后通过Binder来实现参数传递,服务端获取Binder对象并保存IInterface接口对象。
public class Binder implement IBinder{
void attachInterface(IInterface plus, String descriptor)
IInterface queryLocalInterface(Stringdescriptor) //从IBinder中继承而来
...
}
Binder具有被跨进程传输的能力是因为它实现了IBinder接口,系统会为每个实现了该接口的对象提供跨进程传输,Binder具有的完成特定任务的能力是通过它的IInterface的对象获得的,attachInterface方法会将(descriptor,plus)作为(key,value)对存入Binder对象中的一个Map
Stub 继承自Binder并实现了定义的aidl接口类,并作为是aidl接口中的一个内部抽象类。Server 进程需继承 Stub并实例化,同时作为Service#onBind方法返回值传递到客户端,用于初始化 IPC 环境以及负责与Binder驱动通信接收跨进程消息。
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
在服务端进程里实现具体的Stub 时获得Binder对象,并调用attachInterface方法保存IInterface对象。
Stub类中最核心的方法,将Server 端IBinder对象转换为对应的aidl接口实例(com.crazymo.remoteserver.IGetWet),Client端使用时通过Stub.asInterface(binder)转化为具体的接口类对象(代理对象)。若Server和Client端都位于同一个进程,则直接返回Server端的Stub对象本身,否则返回Stub.Proxy代理对象。无论是哪个进程,拿到反序列化的 IBinder 以后,通过这个静态方法来获取。 如果是 Server 进程(local),直接从 binder 中直接取出之前 attach的 IInterface 实例,那么调用 aidl接口的方法就相当于直接调用Stub 实例的方法了;而如果是 Client 进程,IBinder 只是系统在远程创建的一个 Proxy 类并无实现,因此,iin 将变成 null,此时 asInterface 会创建一个Stub.Proxy 代理类并实现 aidl接口。
public static com.crazymo.remoteserver.IGetWet asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.crazymo.remoteserver.IGetWet))) {
return ((com.crazymo.remoteserver.IGetWet)iin);
}
return new com.crazymo.remoteserver.IGetWet.Stub.Proxy(obj);
}
传入DESCRIPTOR作为key并调用queryLocalInterface(DESCRIPTOR)方法,查找到对应的IInterface对象,然后判断对象的类型,如果是同一个进程调用则返回IBookManager对象,由于是跨进程调用则返回Proxy对象,即Binder类的代理对象。
该方法运行在服务端中的Binder线程池中,服务端通过code确定客户端所请求的目标方法是什么,从data中取出目标方法所需的参数,然后执行目标方法,并将目标方法的返回值写入reply。如果该方法的返回false,则表示客户端的请求回失败,可以利用这个特性来做权限验证。
客户端真正使用的对象,Stub.Proxy实现aidl接口,将方法的标识,参数和返回值对象传入方法mRemote.transact中,发起远程调用请求,同时挂起当前线程,然后服务端的Stub#onTransact被调用,当onTransact返回true,调用成功,从reply对象中取出返回值,返回给客户端调用方,当Client 进程使用时通过 Stub.asInterface 创建,同时会将方法调用的数据,都通过 mRemote 转发给Server端的 Binder 实例。
Binder 基于C/S 架构,AIDL是快速实现Binder的一种机制。Client 可以向远程Server 发起一个附带数据的交易 (transact)并可以接收到响应。其中Stub 类(Binder对象/代理)在 Server 进程中实例化,并通过 bindService方式(通过Service.onBind回调)或者其他跨进程通讯方式,将 IBinder 接口传递给 Client,并通过 Service.onBind 方法传递给 Client 进程。
Client 进程接收到 IBinder 以后,通过 Stub.asInterface 方法转换成对应的aidl接口实例,Client 持有反序列化出的 IBinder实例(并不是Binder而是BinderProxy),然后借助这个IBinder实例与Server进行交易(通信)。当Stub.asInterface 在本地进程工作时,则返回 Stub 实例。反之,创建一个 Proxy 实例来代理通讯。
除了包含普通的接口方法,还可以根据业务创建对应用于声明的Parcelable实体类的aidl文件。如果自定义的Parcelable对象,还必须创建一个和它同名的AIDL文件,并在其中声明它为具体的Parcelable类型。
主要就是实现Parcelable接口并重写writeToParcel方法等。
创建一个Service用来监听客户端的连接请求。然后在Service中实现Stub 抽象类(继承自aidl接口中),并实现业务方法的具体逻辑,当客户端连接服务端,会自动回调Service的onBind回调方法,并把Binder对象返回到客户端。
继承Service 重写onBind方法,且在onBind中返回具体的Binder对象(第四步的实例)
首先将服务端工程中的aidl文件夹下的内容(一模一样的路径和结构)整个拷贝到客户端工程的对应位置下,若在一个应用中,就不需要拷贝了,其他情况一定不要忘记这一步。
在ServiceConnection#onServiceConnected回调中通过Stub.asInterface方法把序列化的Binder对象转为具体的aidl接口实例。
private ServiceConnection conn= new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//通过服务端onBind方法返回的binder对象得到aidl接口的实例,得到实例就可以调用它的方法了
mRemoteApi = IBookManager.Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteApi = null;
}
};
Client通过bindService来Server端对应的Binder对象/代理,并在ServiceConnection的onServiceConnected回调中获取真正的aidl接口实例。具体地在Activity中通过bindService()方法绑定远程服务,本质上是通过ContextImpl的bindService()方法。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
ContextImpl#bindServiceCommon
1597 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
1598 handler, UserHandle user) {
1599 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1600 IServiceConnection sd;
1601 if (conn == null) {
1602 throw new IllegalArgumentException("connection is null");
1603 }
1604 if (mPackageInfo != null) {
1605 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
//返回ServiceConnection对应的binder对象,这个在客户端也有保存,避免一个客户端重复生成binder对象(首次创建,本地保存起来)
1606 } else {
1607 throw new RuntimeException("Not supported in system context");
1608 }
1609 validateServiceIntent(service);
1610 try {
1611 IBinder token = getActivityToken();
1612 if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
1613 && mPackageInfo.getApplicationInfo().targetSdkVersion
1614 < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
1615 flags |= BIND_WAIVE_PRIORITY;
1616 }
1617 service.prepareToLeaveProcess(this);
1618 int res = ActivityManager.getService().bindService(
1619 mMainThread.getApplicationThread(), getActivityToken(), service,
1620 service.resolveTypeIfNeeded(getContentResolver()),
1621 sd, flags, getOpPackageName(), user.getIdentifier());
1622 if (res < 0) {
1623 throw new SecurityException(
1624 "Not allowed to bind to service " + service);
1625 }
1626 return res != 0;
1627 } catch (RemoteException e) {
1628 throw e.rethrowFromSystemServer();
1629 }
1630 }
一路深追下去,还是会经过AMS 通过Binder 机制完成通绑定,绑定成功后会回调onBinder()方法,远程服务绑定成功后,会回调onServiceConnected()方法,通知当前Client进程,远程服务已经连接成功,可以获取远程服务的代理进行进程间的通信了。
Service的bindService 由于涉及到AMS、还有ActivityService等众多核心知识点,篇幅问题不便展开。
获得了Binder类的代理对象,并且通过代理对象获得了IInterface对象,那么就可以调用接口的具体实现方法了,来实现调用服务端方法的目的。
@Override
public float getWet() throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
/**
*第一个参数,识别调用哪一个方法的ID
*第二个参数,序列化后的参数(序列化后就是2 进制byte字符串)
*第三个参数,调用方法后返回的数据
*/
boolean _status = mRemote.transact(Stub.TRANSACTION_getWet, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getWet();
}
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
Proxy对象中的transact调用发生后,系统意识到Proxy对象想找它的真身Binder对象(系统其实一直存着Binder和Proxy的对应关系)。于是系统将这个请求中的数据转发给Binder对象,Binder对象将会在onTransact中收到Proxy对象传来的数据,于是它从data中取出客户端进程传来的数据,又根据第一个参数确定想让它执行具体操作,Binder线程中onTransact被触发,于是它就执行了响应操作并把结果写回reply。
transactNative()方法最后会调用远程服务Stub的onTransact()方法,而onTransact()方法会通过asBinder()方法获取远程服务实例并调用对应的真正的实现方法。
case TRANSACTION_getWet:
{
data.enforceInterface(descriptor);
//调用服务端对象实例去真正完成具体的操作
float _result = this.getWet();
reply.writeNoException();
reply.writeFloat(_result);
return true;
}
最后在transact方法获得_reply并返回结果,客户端线程被唤醒。因此调用服务端方法做耗时操作时,应开启子线程,防止超时导致ANR。
获取Stub获取远程服务的代理Stub.Proxy。
通过代理Stub.Proxy调用远程服务的方法,代理Stub.Proxy通过asBinder()方法获取BinderProxy调用transact()方法通知远程服务,而transact()方法使用JNI技术调用了native方法transactNative()方法。
transactNative()方法最后会调用远程服务中Stub的onTransact()方法,而onTransact()方法会通过asBinder()方法获取远程服务实例并调用对应的方法。
AIDL 并不等于Binder,也不并不是Android Developer使用Binder的唯一方式,AIDL 文件只是帮我们把transact()方法的调用给隐藏起来,AIDL声明的每一个方法最终都还是会通过这个transact()方法与另一个进程的代理对象进行通信。
未完待续…