AIDL与Binder
https://developer.android.com/guide/components/aidl?hl=zh-cn#PassingObjects
Android中,Binder是ipc标准方案,AIDL是Google为了方便应用开发者使用ipc而提供的ipc描述语言。使用google提供的工具,能够根据AIDL生成ipc调用的标准IInterface、标准Stub与标准RemoteProxy,其中:
- IInterface定义了ipc方法调用
- Stub是服务端ipc方法实现,是一个抽象类,开发者应该继承该类并实现IInterface定义的ipc方法
- 在抽象类中提供了onTransact方法用于将来自RemoteProxy的方法调用指令翻译转换为对IInterface定义的方法的调用
- 根据AIDL中参数的in/out/inout修饰,来从RemoteProxy传来的Parcelable中解析参数(对应由in修饰的参数)和在方法调用完成后回填回传的Parcelable(对应由out修饰的参数)
- 提供了asInterface静态方法,方便开发者将一个IBinder包装为IInterface
- 当使用Binder做ipc时,IBinder是一个Proxy对象,asInterface将IBinder包装为RemoteProxy
- 当使用Binder做进程内调用时(比如Service和Activity都在主进程中,Activity通过bindService获得了IBinder。。这是Binder机制提供的灵活性,如果你不想让Service运行在独立进程了,修改Service配置即可,不过此时最好多加测试和检查,确保原本运行在独立进程中的Service的代码逻辑不会干扰主进程的代码逻辑),IBinder就是Stub
- RemoteProxy是一个对开发者完全透明的类,当使用Stub定义的asInterface方法时,如果IBinder是来自另一个进程的Proxy,asInterface就会将那个Proxy包装成实现了IInterface的RemoteProxy对象返回
Parcelable
https://developer.android.com/reference/android/os/Parcel.html?hl=zh-cn
Binder机制使用Parcel对象传输数据,Binder可以跨进程传输六个类型的数据,这些数据都是通过Parcel传输的:
- 基础类型
- 基础类型数组
- Parcelable:实现了Parcelable接口的对象可以直接通过Parcel传递,可以通过writeParcelable方法将Parcelable对象的类型信息和对象序列化内容写入,也可以通过writeTypedObject直接将Parcelable对象序列化内容写入,但此时需要在通过readTypedObject读取时指定Parcelable.Creator,即在Parcelable类定义中的CREATOR对象;
- Bundle
- Active Objects:Active Object是对变量的引用,当Active Object在通过Parcel来回传递时,在同一个进程中总是对应同一个变量引用,通过Parcel传递的是一个代表Active Object的Token,每个进程中都维护着Token和对象引用的关系,目前支持的Active Object包括IBinder对象和fd对象:
- 任何IBinder对象都可以被写入Parcel,接收方要么接收到的是原始的IBinder对象(当接收方和最初的发送方在同一个进程),要么接收到的是一个特殊可以通过ipc调用IBinder上定义的方法的代理,由于IBinder的原始发送方进程始终都可以获取同一个对象的引用,Android系统中大量的使用IBinder对象作为token和某个对象做关联,通过关联的IBinder token即可查找到唯一的对象。
- todo: 跨越进程的Binder常常导致内存泄漏问题:如果一个对象被另一个进程引用,那么什么时候应该释放这个对象呢?很多时候这种对象不会被释放,如果这个对象以某种方式引用了Activity或者其他可能导致大量内存不释放的对象,就比较麻烦了。LeakCanary在各个版本的Android系统都检测出了不少类似的系统服务导致的内存泄漏。。。
- FileDescriptor对象也可以被写入Parcel,读出来的是ParcelFileDescriptor对象。当写入和读取方处于不同的进程时,虽然返回的对象中的文件描述符虽然和发送方的不是同一个,但Kernel中为这两个进程的对应fd关联到的底层文件是同一个,并且它们的初始position也是一样的。使用这种机制可以传递一个pipe文件给另一个进程,通过ipc为媒介实现跨进程的pipe文件不落地的跨进程管道通信。
- 任何IBinder对象都可以被写入Parcel,接收方要么接收到的是原始的IBinder对象(当接收方和最初的发送方在同一个进程),要么接收到的是一个特殊可以通过ipc调用IBinder上定义的方法的代理,由于IBinder的原始发送方进程始终都可以获取同一个对象的引用,Android系统中大量的使用IBinder对象作为token和某个对象做关联,通过关联的IBinder token即可查找到唯一的对象。
- 部分标准Java容器