Android进程间通信之AIDL(二)—— 源码简要分析
上一篇我们了解到了如何书写AIDL让客户端和服务端通信,那么接下来我将简单介绍一下其工作原理:
1、首先我们创建一个AIDLAnalysisInterface.aidl文件
// AIDLAnalysisInterface.aidl package com.zui.lib; import com.zui.lib.data.Worker; interface AIDLAnalysisInterface { void analyze(in Worker worker_in, out Worker worker_out, inout Worker worker_inout); }
对于定义的aidl文件,android studio会自动的生成一个相同文件名的java文件,如下图
接下来我们来看看studio生成的AIDLAnalysisInterface.java的内容
/* * This file is auto-generated. DO NOT MODIFY. * Original file: F:\\github\\android\\practice_project\\AIDL\\app\\src\\main\\aidl\\com\\zui\\lib\\AIDLAnalysisInterface.aidl */ package com.zui.lib; /**AIDLAnalysisInterface类由一个静态抽象类Stub和我们定义的analyze()接口组成 */ public interface AIDLAnalysisInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.zui.lib.AIDLAnalysisInterface { //Stub类名是永远固定的写法,我们在服务端service里面实现的也就是这个内部类 private static final java.lang.String DESCRIPTOR = "com.zui.lib.AIDLAnalysisInterface"; // 标识,也就是AIDLAnalysisInterface的包名 + 类名 /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.zui.lib.AIDLAnalysisInterface interface, * generating a proxy if needed. */ //此方法就是返回此Stub对象或者Stub对象的代理,我们在客户端成功绑定service后,在onServiceConnect中会调用到此方法: // mAIDLAnalysisInterface = AIDLAnalysisInterface.Stub.asInterface(service) public static com.zui.lib.AIDLAnalysisInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.zui.lib.AIDLAnalysisInterface))) { /** 进程内通信的话就会返回自身 */ return ((com.zui.lib.AIDLAnalysisInterface) iin); } return new com.zui.lib.AIDLAnalysisInterface.Stub.Proxy(obj); /** 进程间通信的话返回Stub的代理Stub.Proxy */ } @Override public android.os.IBinder asBinder() { return this; } /** 返回当前Stub对象 */ /** * 此方法只有在跨进程通信的时候才会用到,onTransact 这个方法 就是运行在Binder线程池中的,一般就是客户端发起请求,然后android底层代码把这个客户端发起的请求 封装成3个参数 来调用这个onTransact方法, * @param code 表示请求的是哪个方法,用int类型的值标识服务端定义的接口 * @param data 客户端请求的所有参数会被封装成data输入对象 * @param reply 客户端请求方法的输出对象,与data相对应 * @param flags 一般都是0 * @return 一般返回值为ture,如果返回false了,就说明执行失败了 * @throws android.os.RemoteException */ @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_analyze: { /** 客户端调用我们定义的AIDL接口 void analyze(in Worker worker_in, out Worker worker_out, inout Worker worker_inout)时,会进此case*/ data.enforceInterface(DESCRIPTOR); com.zui.lib.data.Worker _arg0; /** _arg0第一个参数也就是对应的 worker_in参数 */ if ((0 != data.readInt())) { _arg0 = com.zui.lib.data.Worker.CREATOR.createFromParcel(data); /** 从data流里读传入的worker_in参数信息,这里可以看到定向tag为in时,客户端会将参数对象的完整信息传入服务端 */ } else { _arg0 = null; } com.zui.lib.data.Worker _arg1; /** _arg1 对应的worker_out参数 */ _arg1 = new com.zui.lib.data.Worker(); /** 创建新对象,可以看出参数的定向tag为out时,客户端并没有将worker_out的完整信息传入服务端,而是新建一个worker_out类型的对象*/ com.zui.lib.data.Worker _arg2; /** _arg2 对应的worker_inout参数 */ if ((0 != data.readInt())) { _arg2 = com.zui.lib.data.Worker.CREATOR.createFromParcel(data); /** 参数定向tag为inout时,同样也会从data流中读入传参信息*/ } else { _arg2 = null; } this.analyze(_arg0, _arg1, _arg2); /** 这里开始执行真正的逻辑,调用服务端的具体实现 */ reply.writeNoException(); /** reply 是执行服务端执行具体逻辑后的输出,对于客户端传入参数的修改都会封装在reply对象中*/ if ((_arg1 != null)) { /** _arg1 的定向tag为out,表示客户端会同步服务端的修改,也就是说服务端如果修改了worker_out,客户端也会体现出来 */ reply.writeInt(1); _arg1.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } if ((_arg2 != null)) { /** _arg2的定向tag为inout,表示客户端传入的worker_inout参数也会同步服务端对它的的修改 */ reply.writeInt(1); _arg2.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } /** 这里并没有从输出流reply对象里面读_arg0参数信息,说明定向tag为in的参数,客户端并不会同步服务端对它的修改 */ return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.zui.lib.AIDLAnalysisInterface { /** 跨进程通信会返回此代理对象 */ 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 void analyze(com.zui.lib.data.Worker worker_in, com.zui.lib.data.Worker worker_out, com.zui.lib.data.Worker worker_inout) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); /** 这里一进来就创建两个对象_data 和 _reply */ android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((worker_in != null)) { _data.writeInt(1); /** 定向tag为in 和 inout的参数,会被写入data流中 */ worker_in.writeToParcel(_data, 0); } else { _data.writeInt(0); } if ((worker_inout != null)) { _data.writeInt(1); worker_inout.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_analyze, _data, _reply, 0); /** 调用了transact这个方法 来发送rpc请求,当前线程会被挂起,服务端的onTransace方法被调用(跨进程的情况),调用结束以后 当前线程继续执行,直到从_reply中取出rpc的返回结果 然后返回_reply的数据 */ _reply.readException(); if ((0 != _reply.readInt())) { /** 定向tag为out 和 inout的参数,服务端对他们的修改会被写入reply流中,客户端通过此reply对象同步服务端的修改 */ worker_out.readFromParcel(_reply); } if ((0 != _reply.readInt())) { worker_inout.readFromParcel(_reply); } } finally { _reply.recycle(); /** 回收 _reply 和 _data 对象*/ _data.recycle(); } } } static final int TRANSACTION_analyze = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); /** 给不同的方法定义的标识*/ } public void analyze(com.zui.lib.data.Worker worker_in, com.zui.lib.data.Worker worker_out, com.zui.lib.data.Worker worker_inout) throws android.os.RemoteException; }
简单总结一下就是:客户端成功绑定service后,获取到服务端的实现的Stub对象,然后客户端可以调用服务端的接口时,首先会创建_data 输入对象,_reply输出对象,然后将客户端传入的参数封装到_data中,接着调用transact()方法,如果是跨进程通信,此时服务端的onTransace()方法会被调用,当调用结束后,会从_reply中取出rpc的返回结果 然后同步_reply的数据。
同时,通过对源码分析,可以看出AIDL传参数的定向tag:in、out、inout的具体含义以及它们之间的区别。
以上就是aidl源码的大致分析