Binder 涉及面太广,这里先介绍一下进程间的数据传递载体——Parcel,对我们后面学习 Binder 通信有很大的帮助。在学习 Binder 通信之前,我们要问自己,为什么需要 Binder, 它究竟是怎么实现的?
其实我们费这么大的劲还是为了进程间通信,这里举个不是很恰当的例子。 有 A 、B 两个小岛,中间是分隔开的,A 岛上的人想吃 B 岛上特有的水果,怎么办? 这里就得有很多分工,还得有工具,下面我们开始展开。 这里很重要的一点是,A 岛要拿到 B 岛上一模一样的水果,同一个对象,不要仿制品。 这个很重要,就像我们吃海鲜,要吃从日本,啊不......打包好的海鲜。讲究的是同一个对象。
A 岛上根据水果店的地址去买就好了,但是要是给了 B 岛上的水果店地址,A 岛具名就没有那么简单买到了。 这里我们需要折腾一下。
同一个进程间的对象传递都是通过引用来做的,因而本质上就是传递了一个内存地址。这种方式在跨进程的情况下就无能为力了,由于采用虚拟内存机制,两个进程中都有自己独立的内存地址空间,所以跨进程传递的地址值是无效的。(那咋办呢?)
Parcel
具有打包和重组的能力。这里可以把它想象成一个集装箱。
-
Parcel 设置相关
dataSize( ) : 获取当前已经存储的数据大小
dataPosition( ): 数据的当前位置值,有点类似于游标
-
Primitives
原始类型数据的读写操作, 用哪种方式写入的数据就要相应的方式正确读取。
数据是按照 host cpu 的字节序来读写的。
writeByte(byte): 写入一个 byte
readByte( ): 读取一个 byte
-
Primitive Arrays
原始数据类型数组的读写操作通常是先写入用 4 个字节表示的数据大小值,接着再写入数据本身。
注意: 如果写入数据时系统已经超出了 Parcel 的存储能力,它会自动申请所需的内存空间,并扩展 dataCapacity, 而且每次写入都是从 dataPosition() 开始的。
-
Parcelables
遵循 Parcelables 协议的对象可以通过 Parcel 来存取
-
Bundles
Bundle 继承自 Parcelable ,是一种特殊的 type-safe 的容器。
最大的特点是采用键值对的方式来存取数据。
-
Active Objects
写入的是它们的特殊标志引用。在 Parcel 中读取这些对象时,大家看到的并不是重新创建的对象实例,而是原来那个被写入的实例。(这个就非常神奇了)
以这种方式传输的对象主要有2类:
- Binder
android 系统核心的 IPC 通信机制,同时 也是一个对象。
利用 Parcel 将 Binder 对象写入,读取时就能得到原始的 Binder 对象,或者是它的特殊代理实现(最终操作的还是原始 Binder 对象)
writeStrongBinder(IBinder)
writeStrongInterface(IInterface)
readStrongBinder()
......
- FileDescriptor
是 Linux 中的文件描述符,可以通过 Parcel 的如下方法进行传递。
writeFileDescriptor(FileDescriptor)
readFileDescriptor()
因为传递后的对象仍然会基于和原对象相同的文件流进行操作,所以可以认为是 Active Object 的一种。
- Untyped Containers
用于读写标准的任意类型的 java 容器。
writeArray(Object[])
readArray(ClassLoader)
writeList(List)
readList(List, ClassLoader)
......
这里很厉害的地方就是,Parcel 能够根据协议(打包和重组所用的协议必须是配套的)来为接收方提供完整还原出原始数据对象的业务。
这里流程比较复杂,但是其实原理还是为了跨进程通信,Parcel 有这样的能力。