从Android Aidl (一)通过实例初探Aidl一文中,我们通过简单的实例,来了解了Aidl在进程与服务之间的通信。
接下来,我们来回答一下,上篇文章我们提出的问题。
具体实现:
1.回顾调用流程
1.1 App1 通过intent与远处的Service关联上,并调用bindService方法绑定。
intent.setPackage("com.example.myapplication2");
intent.setAction("com.example.myapplication2.DownloadService");
this.bindService(intent,conn, Context.BIND_AUTO_CREATE);
1.2 连接服务后,会调用onServiceConnected方法,重要的是做了两件事
- 通过DownloadAidlInterface.Stub.asInterface(iBinder)获取服务端的DownloadAidlInterface类。
- 调用在App2实现的方法getProgress来获取模拟的下载进度。
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
final DownloadAidlInterface aidlInterface = DownloadAidlInterface.Stub.asInterface(iBinder);
...
int pro = aidlInterface.getProgress();
...
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
有关服务的可以参考文章。
到这里,我们就能感受到,App1进程就像在操作自己定义的类对象一样,通过类方法获取类的成员变量。但是实际是调用远处的Service的类。
2.客户端
究竟客户端为什么可以获取DownloadAidlInterface的实例,我们来看看由aidl文件生成的java文件,路径在
我们来分析这个java文件
首先调用的是DownloadAidlInterface.Stub.asInterface(iBinder)
public static abstract class Stub extends android.os.Binder implements com.example.myapplication2.DownloadAidlInterface
{
public static com.example.myapplication2.DownloadAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.myapplication2.DownloadAidlInterface))) {
return ((com.example.myapplication2.DownloadAidlInterface)iin);
}
return new com.example.myapplication2.DownloadAidlInterface.Stub.Proxy(obj);
}
}
如果iin为空,会创建一个新的Proxy代理对象,我们再继续往下看。
private static class Proxy implements com.example.myapplication2.DownloadAidlInterface
{
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 int getProgress() 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);
mRemote.transact(Stub.TRANSACTION_getProgress, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
到这一步我们应该可以确定,我们通信的是Proxy这个代理类,也看到了getProgress()方法。(因为我也在调试的时候进入了这里嘿嘿)
我们分析一下getProgress(),不然有点不知所云。
至于继续往下探索,就是有关binder等的Android的进程的通信机制了,通信机制后面我自己也会继续写,我们先把结论总结出来。
3.服务端
我们在同样的DownloadAidlInterface.java文件中,能找到onTransact()方法。
- _data和_reply属于Parcel类,其实是容器,是用来存放数据和读取数据的。在这里,是用来存放和读取在客户端与服务器之间通信的数据。Parcel.obtain()是从容器池里获取容器。_reply.readInt()为读取整型数据,对应服务端的getProgress的返回值类型,同为Int型。recycle()为回收Parcel内存。
- transact() 方法为客户端与服务器通信的核心方法。执行此方法后,客户端会挂起,等待服务端的数据流回复,返回到_reply的类变量。
@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_getProgress:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getProgress();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
实际此方法对应的是在客户端Transact()方法。原理也还是前面说的Android的通信机制,这里我们先给出结论。
- 从客户端发来的code,进入了TRANSACTION_getProgress的switch case里面。明显的,在这里调用了this.getProgress()方法,这个this就是继承了我们接口“com.example.myapplication2.DownloadAidlInterface”,调用的结果保存给_result变量。然后调用这里的reply.writeInt(_result)方法,从这里把数据写了进去。能看出来,对应的是客户端里调用了reply.readInt()方法。
4.总结
到这里,客户端和服务端的通信已经介绍完毕了。我们用一张图来总结整个调用流程,调用流程为1-2-3。
为了加深对Aidl的认识,我们再来看一下,为什么要用Aidl的方式。
在软件的设计领域类,任何的解决或者优化方案,都是围绕这需求和性能来作文章,所以从这个基础上看:
- 实现基础功能IPC。
- 在功能实现的基础上,对开发者友好,即方便开发。
所以在此基础上,建立一套模板,把涉及到数据流通信的复杂流程封装起来。对外保留易于理解的接口供开发者使用,这就为对开发者友好。