AIDL
AIDL是是Android Interface Definition Language的简称,翻译过来就是安卓进程间通信语言。既然是一门“语言”,就有相应的“语法”。
这个AIDL“语言”严格来说肯定不算一门语言,它只是规定了一些简单的“语法”,开发者遵循这些“语法”,就能比较方便、快捷地完成进程间通信的开发工作,其实说它是一个插件更合适。
从根本上来说,Android进程间通信的核心是Binder,我们完全可以利用Binder来完成进程间通信,但开发起来比较复杂、可读性差。Google为了方便我们开发,就提供了AIDL这种插件,我们只需要遵循一些“语法”规则,就能完成进程间通信了。
AIDL使用简单介绍
第一步,在服务端app中,src/main目录下创建aidl目录,创建com.wk.kupart包,在该包下创建Fun.aidl文件,并声明两个方法:
package com.wk.kupart;
interface Fun {
int add(in int x, in int y);
int reduce(in int x, in int y);
}
第二步,clean工程,clean完成后,build/generated/source/aidl/debug目录下就会自动生成com.wk.kupart包,该包下有个Fun.java文件,这就是系统根据上面的Fun.aidl文件自动生成的java文件;
第三步,创建MyService类继承自Service,实现onBind()方法,在该方法中返回Fun.stub对象:
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// 返回Fun.Stub对象。从自动生成的Fun.java文件中可以看到,Fun.Stub类实现了IBinder接口
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
// new出Stub实例,并实现接口(就是我们自己定义的两个方法)
private final Fun.Stub mBinder = new Fun.Stub() {
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
@Override
public int reduce(int x, int y) throws RemoteException {
return x - y;
}
};
}
第四步,在客户端app中,重复上面的步骤一和步骤二。注意包名、类名、声明的方法等要跟服务端app中的一模一样;
第五步,客户端app绑定远程Service,在onServiceConnected()方法中就可以拿到远程Service的“句柄/引用”了:
public class NewActivity extends Activity {
private Fun mFun;
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 通过asInterface()方法把IBinder实例转换成Fun实例,就可以调用Fun中定义的方法了
mFun = Fun.Stub.asInterface(service);
try {
int x = mFun.add(2, 5);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
这样,利用AIDL语法完成的进程间通信就完成了,接下来,我们分析下工作原理。
AIDL源码分析
1、对于跨进程的情况(同进程情况后面会说),下面几点一定要记住:
- 通过定义aidl文件、clean工程生成class文件之后,客户端、服务端都有了Fun类和各自的Fun对象,客户端调用的是客户端的Fun对象,服务端调用的是服务端的Fun对象,只是它们的方法、成员变量一模一样而已,不要搞混了(android跨进程是无法共享内存的);
- 客户端绑定service成功后,service的onBind()方法会返回IBinder对象,但是这个对象只存在服务端进程中,无法传递到客户端进程;
- 客户端绑定成功后,onServiceConnected()方法中的参数IBinder,是客户端new的一个IBinder实例,不是服务端返回的。但是通过这个IBinder,可以把指令发送到远程服务端;
2、分析Fun.java源码前,要搞清楚几点,方便分析:
- Fun继承自IInterface;
- Stub是Fun的内部类,继承了Binder类,实现了Fun接口。也就是说,Stub具有这两个类以及IInterface的特性,可以被当成Binder、Fun、IInterface实例使用;
- Proxy是Stub的内部类,实现Fun接口。也就是说Proxy具有了Fun的特性,可以被当做Fun、IInterface实例使用;
3、下面看源码分析,就以注释形式给出,方便阅读。
public interface Fun extends android.os.IInterface
{
// Fun的静态内部类,继承自Binder,实现了Fun接口(即实现了我们在Fun.aidl中声明的方法)
public static abstract class Stub extends android.os.Binder implements com.wk.serviceapp.Fun
{
private static final java.lang.String DESCRIPTOR = "com.wk.serviceapp.Fun";
// 通过int值来标识客户端调的到底是哪个方法
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_reduce = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
// Stub的构造方法
public Stub() {
// attachInterface()是Binder的方法,作用是把IInterface 和 DESCRIPTOR“键值对”保存起来
this.attachInterface(this, DESCRIPTOR);
}
// 通过IBinder对象,生成Fun对象
public static com.wk.serviceapp.Fun asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
// queryLocalInterface()方法就是通过key(即DESCRIPTOR)获取对应的value(即IInterface),查询到的结果就是在构造方法中保存的“键值对”
// 对于同进程的Service,在创建Stub的时候就把“键值对”保存起来了,所以这里可以获取到对应的value
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.wk.serviceapp.Fun))) {
return ((com.wk.serviceapp.Fun)iin);
}
// 对于跨进程Service,就会new一个Proxy对象返回(Proxy继承自Fun)
return new com.wk.serviceapp.Fun.Stub.Proxy(obj);
}
// 客户端调用add()方法后,客户端的IBinder会把指令、参数发送到服务端,服务端通知对应的的IBinder,并调用其onTransact()方法
// 由此可以看出IBinder机制底层主要就是实现了:客户端IBinder发出的消息能准确到达服务端对应的IBinder。有了这个能力,跨进程通信就很容易了
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
// 解析参数、flag,调用对应的方法,跟Proxy一样(不懂的可以看下面Proxy的注释),就不重复了
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();
// 这里的this就是Stub对象,而Stub对象是在Service中创建的,创建的时候实现了add()接口
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_reduce: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.reduce(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// Proxy是Stub的内部类,也实现了Fun接口,所以可以被当作Fun类型返回
private static class Proxy implements com.wk.serviceapp.Fun
{
// 绑定Service成功后,客户端创建的的IBinder对象
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
// 可以看到,客户端调用add()方法时,就是调用的Proxy中的add()方法
@Override public int add(int x, int y) throws android.os.RemoteException
{
// 远程调用时的参数记录在_data中,执行返回的结果会记录在_reply中
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 将参数x、y记录到_data中
_data.writeInt(x);
_data.writeInt(y);
// 调用mRemote的transact()方法,把“调用的是哪个方法”、“参数”、“存放执行结果的容器”传过去
// transact()方法又会回调mRemote的onTransact()方法,实现真正地调用,分析到服务端时再分析
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
// 服务端执行完毕后,把结果放在_reply“容器”中,然后从_reply中取出结果
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
// 返回服务端的执行结果
return _result;
}
@Override public int reduce(int x, int y) 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(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_reduce, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
public int add(int x, int y) throws android.os.RemoteException;
public int reduce(int x, int y) throws android.os.RemoteException;
}
自此,一个完成的客户端调用、中间件传递、服务端执行、通过中间件通知客户端流程就分析完毕了。
自定义进程间通信
我们现在已经知道了Binder进程间通信的原理,那我们完全可以自己定义一套,比AIDL更简单、逻辑更清晰。
客户端绑定Service之后只能得到一个IBinder对象,服务端被绑定成功后也只能返回IBinder对象,所以我们自定义也只能基于Binder。
第一步,服务端的实现:
我们知道接收到客户端指令后会调用Binder的onTransact()方法,所以我们只需要自定义onTransact()方法,把执行结果返回给客户端即可。
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// 返回我们自定义的IBinder对象
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
private MyBinder mBinder = new MyBinder();
// 自定义MBinder,集成自Binder
private class MyBinder extends Binder
{
@Override
// 重写onTransact()方法,处理客户端的指令
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException
{
switch (code)
{
// 如果指令code为0x01,就执行加法。这个指令自己定义就行
case 0x01:
{
// 这个是token,客户端调用时需要设置token与下面值一样
data.enforceInterface("com.wk.serviceapp.myservice");
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
// 执行加法
int _result = _arg0 + _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
// 如果指令code为0x02,就执行减法
case 0x02:
{
data.enforceInterface("com.wk.serviceapp.myservice");
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
// 执行减法
int _result = _arg0 - _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
在manifest中注册与AIDL一致,就不说了。
第二步,客户端实现:
客户端要做的更简单,就是调用IBinder的Transact()方法通知服务端就可以了。
public class NewActivity extends Activity {
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
// 设置token
_data.writeInterfaceToken("com.wk.serviceapp.myservice");
// 设置add()方法中的参数1、参数2
_data.writeInt(2);
_data.writeInt(5);
// 调用transact()方法
service.transact(0x01, _data, _reply, 0);
_reply.readException();
// 获取结果
_result = _reply.readInt();
Log.i("wk", "result:" + _result);
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new);
Intent intent = new Intent();
intent.setAction("com.wk.serviceapp.add");
intent.setPackage("com.wk.serviceapp");
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
}
}
就这么简单,一个基于Binder的自定义的进程间通信就完成了,没有aidl文件,没有自动生成的java文件,也不需要注意aidl繁琐的规则。
注意一点,可以看到,客户端调用服务端的方法后,就是同步等待服务端的执行结果,所以不能执行耗时操作。如果要执行耗时操作,就把transact()方法的调用放在子线程中。