前言
上篇文章说到AIDL只是一种接口定义语言,系统会根据它为我们自动创建进行IPC通信的代码。其实所生成的代码是一个Java类,存在于/app/build/generated/source/aidl/debug/[包名]目录下,我们可以简单分析一下到底生成了一些什么代码,这对于理解AIDL的过程很有帮助。
1.自动生成的代码
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/xushuzhan/AndroidStudioProjects/Test/AIDLTest/app/src/main/aidl/com/example/xushuzhan/aidltest/IBookManager.aidl
*/
package com.example.xushuzhan.aidltest;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.xushuzhan.aidltest.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.xushuzhan.aidltest.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.xushuzhan.aidltest.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.xushuzhan.aidltest.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.xushuzhan.aidltest.IBookManager))) {
return ((com.example.xushuzhan.aidltest.IBookManager) iin);
}
return new com.example.xushuzhan.aidltest.IBookManager.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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getBook: {
data.enforceInterface(DESCRIPTOR);
com.example.xushuzhan.aidltest.BookBean _result = this.getBook();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.xushuzhan.aidltest.BookBean _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.xushuzhan.aidltest.BookBean.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.xushuzhan.aidltest.IBookManager {
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public com.example.xushuzhan.aidltest.BookBean getBook() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.xushuzhan.aidltest.BookBean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.xushuzhan.aidltest.BookBean.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.xushuzhan.aidltest.BookBean book) 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 ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
//定义返回书本的方法
public com.example.xushuzhan.aidltest.BookBean getBook() throws android.os.RemoteException;
//定义添加书本的方法,这里的 in 代表数据的流向是:客户端->服务端,服务端对数据的任何修改都不会影响到客户端
public void addBook(com.example.xushuzhan.aidltest.BookBean book) throws android.os.RemoteException;
}
可以看到代码量并不多,只有不到200行,下面就来简单分析一下它。
2.关键方法简单分析
首先可以看到IBookManager中有两个内部类:Stub和Proxy,他们分别扮演着服务端和客户端的角色。
2.1 Stub
Stub类中的方法并没有太多,结合注释分析起来还是完全没有压力。
asInterface
:正如注释中写的那样,这个方法主要用来将IBinder对象转换为IBookManager接口,一般在客户端的ServiceConnection的onServiceConnectrd方法中调用。如果是在同一线程中调用,则会返回Stub对象本身,否则则返回它的代理:Stub.Proxy。
IBookManager mBookManager;
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookManager = IBookManager.Stub.asInterface(service);
try {
Log.i(TAG, "onServiceConnected: "+mBookManager.getBook().getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
asBinder
:很显然,这个方法返回的是当前的Stub对象本身。
onTransact(int code, Parcel data, Parcel reply, int flags)
:这就是一个比较核心的方法了,它主要是用来执行远程调用的目标方法,执行完后把返回值写入reply中。这个方法是运行在服务端的Binder线程池中,客户端发起请求的时候,会通过系统底层封装后交给此方法来处理,如果返回false,那么客户端将请求失败(调用不到所请求的方法)。
它的四个参数的含义也很直观:
code
:即AIDL接口中方法的唯一标识,通过它可以确定调用的是那个方法。
data
:客户端远程调用方法的时候传过来的参数的值被封装进了这个data,需要就可以取出。
reply
:当前方法的返回值会被封装进reply。
flags
:只有0和1(FLAG_ONEWAY)两种取值,取0表示同步操作(默认),取1表示客户端单向操作,无需等待服务端响应。
2.2 Proxy
Proxy实现了IBookManager接口,提供了IBookManager方法中的具体实现,供远程调用,最关键的方法是步骤是:mRemote.transact()
,在IBookManager的具体方法实现中(getBook()和addBook()),最后都会调用它来实现远程请求,同时挂起当前线程,等到远程服务端执行完onTransact()并返回值的时候,当前线程便可继续进行。
大概流程如下:
3.整体流程
还是书上的一张图来得实在,清晰地说明了整个IPC过程。
通过简单地分析,不难看出通过AIDL实现IPC的整体流程,说到底,最核心的部分还是Binder,Stub和Proxy类也是通过Binder协同合作的,一个处理数据,一个写入数据。值得注意的是数据并不是像寄快递一样原封不动地在进程之间传输,而是通过了序列化和反序列传递的是内容一样的但并非同一个的数据。