先从一个简单的 AIDL 看如何跨进程通讯。
定义 MyData,当然其中自己生成 get、set、和 Parcelable 相关方法,可以使用 android studio 插件。
public class MyData implements Parcelable {
private String data;
private int type;
//后续生成aidl时会出现没有这个函数,这边手动创建下,类似这样的。
//可以从MyData的构造方法中拷贝
public void readFromParcel(Parcel in) {
this.data = in.readString();
this.type = in.readInt();
}
}
定义 AIDL
创建 IMyAidlInterface.aidl 文件,这里需要手动将 MyData 的包导入进去,android studio 还不太智能啊
// IMyAidlInterface.aidl
package com.example.aidl;
// Declare any non-default types here with import statements
import com.example.aidl.MyData;
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String basicTypes(boolean aBoolean);
MyData basicMyData(inout MyData data);
}
in:参数由客户端设置,或者理解成客户端传入参数值。
out:参数由服务端设置,或者理解成由服务端返回值。
inout:客户端输入端都可以设置,或者理解成可以双向通信。
MyData.aidl,固定写法,放在与 MyData 同名的包下面,这里不需要导入包了。
// IMyAidlInterface.aidl
package com.example.aidl;
// Declare any non-default types here with import statements
//import com.seabreeze.appstore.MyData;
parcelable MyData;
build 后会生成 IMyAidlInterface 同样类名的接口文件,在 build\generated\aidl_source_output_dir\debug\compileDebugAidl\out 下,找到相同的包名。
public interface IMyAidlInterface extends android.os.IInterface {
}
新建一个 Service,注意要设置进程,因为我这边图简单,直接在一个项目中测试了。
<service android:name=".AidlService"
android:process=".com.example.service"/>
public class AidlService extends Service {
@Override
public void onCreate() {
super.onCreate();
boolean mainProcess = false;
try {
mainProcess = AppProcessUtils.isMainProcess(this);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
Log.e("MainActivity", "AidlService onBind 是否在主进程 : " + mainProcess);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
MyBinder myBinder = new MyBinder();
Log.e("MainActivity", "service : " + myBinder.toString());
return myBinder;
}
static class MyBinder extends IMyAidlInterface.Stub {
@Override
public String basicTypes(boolean aBoolean) throws RemoteException {
return String.valueOf(aBoolean);
}
@Override
public MyData basicMyData(MyData data) throws RemoteException {
int type = data.getType();
data.setType(type + 1);
return data;
}
}
}
在 AndroidManifest.xml 中使用 android:process=".进程名" 可指定进程名字,这里指定的是 com.example.service 进程。
AidlService 继承 Service,我在 onCreate 中判断了 AidlService 是否在项目的主进程中,之后创建 Service 端的 MyBinder 对象,并简单打印了它,将它返回。
MyBinder 继承 IMyAidlInterface.Stub,重写了 basicTypes、basicMyData 方法,并简单的实现了方法。
Service 编辑完成后,下一步启动 Service。
Intent intent = new Intent(this, AidlService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity", "service : " + service.toString());
IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
String s = iMyAidlInterface.basicTypes(true);
boolean mainProcess = AppProcessUtils.isMainProcess(HomeActivity.this);
Log.e("MainActivity", "onServiceConnected方法 是否在主进程 : " + mainProcess);
Log.e("MainActivity", s);
} catch (RemoteException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
这里通过 bindService 启动了 AidlService,在 onServiceConnected 中获取 IMyAidlInterface 对象,这里将得到的 IBinder 类型的 service 也打印了下,之后调用 IMyAidlInterface 的方法 basicTypes、basicMyData。
之后打印了当前方法是否在主进程中。运行下项目看下 log:
项目主进程中
com.example.aidl E/MainActivity: service : android.os.BinderProxy@5619162
com.example.aidl E/MainActivity: onServiceConnected方法 是否在主进程 : true
com.example.aidl E/MainActivity: basicTypes返回值 :true
com.example.aidl E/MainActivity: basicMyData返回值中type = 101
aidl进程中
.com.example.service E/MainActivity: AidlService onBind 是否在主进程 : false
.com.example.service E/MainActivity: service : com.example.aidl.AidlService$MyBinder@2d60faa
这样 aidl 实现了 com.seabreeze.appstore 进程和 aidl 进程之间通信,注意这里的两个 IBinder 对象不一致。
当然这里的 Service 可以使用定义 action 启动,之后启动 Service 后可以根据 action 去启动,这样实现了两个 app 之间的通讯,当然需要将生成的 IMyAidlInterface 接口拷贝过去。
我们看下 Service(AidlService)端,MyBinder 继承自 IMyAidlInterface.Stub 对象,Stub 继承 Binder,同时实现了 IMyAidlInterface 接口。
//Binder机制在Android中的实现主要依靠的是Binder类,其实现了IBinder接口
//IBinder接口:定义了远程操作对象的基本接口,代表了一种跨进程传输的能力
//系统会为每个实现了IBinder接口的对象提供跨进程传输能力,即Binder类对象具备了跨进程传输的能力
public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyAidlInterface";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
}
父类的 Binder 中将传入的 this 和 DESCRIPTOR 赋值给自身的成员变量。
//之后Binder对象 可根据descriptor通过queryLocalIInterface()获得对应IInterface对象,可依靠该引用完成对请求方法的调用
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
看下 asBinder 的实现,它将自身返回。
@Override
public android.os.IBinder asBinder() {
return this;
}
AidlService 是将 IMyAidlInterface.Stub 对象返回。
看下 Client(Activity)中的 asInterface
public static com.example.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IMyAidlInterface))) {
return ((com.example.aidl.IMyAidlInterface) iin);
}
return new com.example.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
queryLocalInterface 方法
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
查询方法很简单,判断 descriptor 即可。如果是自身调用,也就是在同一个进程中,将自身转化为 IMyAidlInterface 并返回。而不是同一个进程,则创建 IMyAidlInterface.Stub.Proxy 的代理对象。我们打印的两次 IBinder 不一致,则是创建了代理对象。
private static class Proxy implements com.example.aidl.IMyAidlInterface {
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;
}
}
代理对象也必须实现 IMyAidlInterface 接口,它在构造方法中将实质的对象保存起来,需要 asBinder 时将其返回。这样在 Client 中就有了 IMyAidlInterface 的引用。
看下它的 basicTypes 方法
MyData myData1 = iMyAidlInterface.basicMyData(myData);
这里 iMyAidlInterface 时代理对象,则会走到代理对象的 basicMyData 方法中
@Override
public com.example.aidl.MyData basicMyData(com.example.aidl.MyData data) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidl.MyData _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((data != null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_basicMyData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().basicMyData(data);
}
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.aidl.MyData.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
data.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先声明 _data 和 _reply 两个 Parcel 对象,依次将将 DESCRIPTOR、1、data 写入 _data 中,调用 mRemote.transact 方法去 service 端请求,之后根据返回的 bool 值进行判断。如果数据为 null 则会写入 0,不会写入 data。
public static com.example.aidl.IMyAidlInterface sDefaultImpl;
public static com.example.aidl.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
public static boolean setDefaultImpl(com.example.aidl.IMyAidlInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
这里没有设置它的实现,当然,我们可以猜测,_status 为 true 表示成功。那么走到下面,从 _reply 中读取数据。当然这里判断 0 或 1 ,那么 service 也是和我们之前写数据一样,有数据为 1,没有数据为 0。然后调用 MyData 中重写的 Creator 的 createFromParcel 方法创建新的对象,最后返回。
我们的 mRemote.transact 方法会到我们创建的 Stub 中 onTransact 方法中
//code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
//data:目标方法的参数
//reply:目标方法执行后的结果(返回给Client进程)
// 注:运行在Server进程的Binder线程池中;当Client进程发起远程请求时,远程请求会要求系统底层执行回调该方法
@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: {
//
}
case TRANSACTION_basicMyData: {
data.enforceInterface(descriptor);
com.example.aidl.MyData _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.aidl.MyData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
com.example.aidl.MyData _result = this.basicMyData(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
在 onTransact 中根据 code 判断,这里时 TRANSACTION_basicMyData,它先验证 descriptor,判断 0 或 1,还原 MyData,和 transact 中顺序一致。
调用 Servie 中重新实现的 basicMyData,得到返回结果 _result,之后将 _result写入到传入的 reply 中,将 _arg0 也写入 reply 中(当然先进行判断,写入 1),最后返回 true,验证了我们的假设。
这样简单的通过 AIDL 就可以实现进程间通讯,下节从源码角度看下 binder 机制。
demo地址:https://github.com/milanxiaotiejiang/Aidl_Demo
再重点说明下 asInterface,在同进程中,onServiceConnected 接收得到的 service 对象的类型为 MyBinder,我们知道表示的是 AidlService 中的内部类,而在 AidlService 的定义中,我们只在 myBinder 的初始化中定义了一个 IMyAidlInterface.Stub() 的子类,即同进程时,在 onServiceConnected 接收到的是 IMyAidlInterface.Stub() 类型。
其 queryLocalInterface 方法来源于超类 android.os.Binder。对于方法中传入的 descriptor,通过 asInterface 的代码可知就是 Stub 中定义的 DESCRIPTOR("com.example.aidl.IMyAidlInterface"),而 Binder 中定义的 mDescriptor,其赋值过程是在 attachInterface 函数中,而 attachInterface 函数是在 Stub 的构造函数中被调用。
在 onServiceConnected 中的调用为 IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
从而我们可知 queryLocalInterface 中 mDescriptor.equals(descriptor) ,判断语句中的 mDescriptor 和 descriptor 都为 IMyAidlInterface.Stub 中定义的 DESCRIPTOR,则 queryLocalInterface 返回的是 mOwer。mOwer 就是 attachInterface 中赋值的,即该 Stub 对象本身。
再来分析跨进程调用的情形,onSericeConnected 中接收到的 service 为 android.os.BinderProxy 类型,BinderProxy 为 final 类,且其 queryLocalInterface 方法直接返回的 null,结合 asInterface 的代码逻辑,就知道它返回的为 IMyAidlInterface.Stub.Proxy 对象。
可以得出结论同进程时,调用 asInterface 返回的是 Stub 对象,其实就是在 onBind 中返回的 mBinder。不同进程时,调用 asInterface 返回的是 Stub.Proxy 对象。