之前已经学习过AIDL的用法和一些需要注意的地方,现在来关注一下AIDL的内部实现过程。
之前只是定义了一个.aidl文件就能让我们进行跨进程通讯了,这实在是很爽,但是还是要去了解一下,系统帮我们做了什么工作。
先看一下.aidl对应的.java的内部结构
package com.charles_lun.db.aidl;
interface IRemoteService{
int getPid();
//参数默认是in
void basicTypes(int anInt,long aLong,boolean aBoolean,
float aFloat,double aDouble,String aString);
}
很清晰的能看到,接口IRemoteService中包含一个静态抽象类Stub.class,还有一个静态代理类Proxy.class,以及两个抽象方法getPid(),basicTypes();
再来看下简单的UML图,这看着是不是似曾相识,这就是代理模式吧。
理清各个接口,类之间的关系,对下面分析代码很有帮助,有助于对逻辑的理解。
AIDL的代码生成器,已经根据.aidl文件自动帮我们生成Proxy、Stub(抽象类)两个类,并且把客户端代理mRemote的transact()过程以及 服务器端的onTtransact()过程默认实现好了,我们只需要在服务器端继承Stub,实现自己的业务类(在onTtransact()中会调用)。
- 代码分析
Proxy运行在客户端,它实现了IRemoteService接口,并且持有一个远程代理IBinder mRemote,mRemote不做任何业务逻辑处理,仅仅通过IBinder接口的transact()方法,把客户端的调用参数序列化后transact到远程服务器。
@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();
}
}
_data为调用接口传入的参数,_reply为调用方法得到的返回值,mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);为调用过程。
再来看Stub.class,继承自Binder,实现IRemoteService接口,核心的方法在onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags);
@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_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
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;
}
}
return super.onTransact(code, data, reply, flags);
}
远程服务端通过IBinder接口的onTransact()来接受数据,处理数据,并且调用真实的逻辑业务代码(IRemoteService中的方法 ),通过传递返回值,而这个真实的逻辑业务需要我们自己去实现。也就是说在AIDL过程中,我们无需关心数据的处理,传递,只需要关注业务的逻辑实现即可。
再来一下,客户端需要调用的方法IRemoteService.Stub.asInterface(IBinder)
public Stub()
{ //绑定Binder和IInterface
this.attachInterface(this, DESCRIPTOR);
}
public static com.charles_lun.db.aidl.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.charles_lun.db.aidl.IRemoteService))) {
return ((com.charles_lun.db.aidl.IRemoteService)iin);
}
return new com.charles_lun.db.aidl.IRemoteService.Stub.Proxy(obj);
}
先通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那么就不需要跨进程了,直接将IRemoteService当做普通的对象来使用,否则会返回远程对象的代理对象。
可能有人要问queryLocalInterface(String)里面干的啥,那我们就进去看看呗:
Binder.java
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
* to return the given owner IInterface when the corresponding
* descriptor is requested.
*/
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
这下很明显了吧,attachInterface()将IInterface接口的实现(这里的this指的是AIDLService这个服务)与Binder绑定在一起,然再通过这个Tag去Binder(这个是客户端传过来的)中查询(这里查询结果的是BinderProxy)。很显然两个查出来的不会是同一个东西,如果不好理解,再看下上面的UML图。
看一下,我做的实验:
public Stub()
{
System.out.println(" attatch....."+DESCRIPTOR+",this:"+this.toString());
this.attachInterface(this, DESCRIPTOR);
}
public static com.charles_lun.db.aidl.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
System.out.println("obj:"+obj.toString()+"is iin null:"+(iin == null)+",instanceof IRemoteService:"+(iin instanceof com.charles_lun.db.aidl.IRemoteService));
if (((iin!=null)&&(iin instanceof com.charles_lun.db.aidl.IRemoteService))) {
return ((com.charles_lun.db.aidl.IRemoteService)iin);
}
return new com.charles_lun.db.aidl.IRemoteService.Stub.Proxy(obj);
}
这个测试中,其余代码都是一样,只是改变AIDLService的运行进程
先设置为跨进程,看下打印结果,两个IInterface的实现不一样吧
12-02 16:31:53.600: I/System.out(1448): 1448
12-02 16:31:53.689: I/System.out(1470): attatch.....com.charles_lun.db.aidl.IRemoteService,this:com.charles_lun.db.aidl.AIDLService$1@2b47e7b
12-02 16:31:53.689: I/System.out(1470): on create....
12-02 16:31:53.689: I/System.out(1470): on bind....
12-02 16:31:53.720: I/System.out(1448): connect.....thread:main
12-02 16:31:53.720: I/System.out(1448): obj:android.os.BinderProxy@2046880dis iin null:true,instanceof IRemoteService:false
在看设置为非跨进程,打印结果:这个是一样的
12-02 16:33:50.212: I/System.out(2352): 2352
12-02 16:33:50.234: I/System.out(2352): attatch.....com.charles_lun.db.aidl.IRemoteService,this:com.charles_lun.db.aidl.AIDLService$1@35cf253f
12-02 16:33:50.234: I/System.out(2352): on create....
12-02 16:33:50.234: I/System.out(2352): on bind....
12-02 16:33:50.270: I/System.out(2352): connect.....thread:main
12-02 16:33:50.271: I/System.out(2352): obj:com.charles_lun.db.aidl.AIDLService$1@35cf253fis iin null:false,instanceof IRemoteService:true
这个没骗人吧,两个都是AIDLService,有数据作对比。
说了那么多有点晕了吧,没事,别晕,放一张流程图,是从别人那里盗来的,是不是有点条理了。鉴于水平有限,如果有不正确或者不当的地方,请大大们指正。