Binder(二)| 进程间的数据传递载体——Parcel

84 阅读3分钟

Binder 涉及面太广,这里先介绍一下进程间的数据传递载体——Parcel,对我们后面学习 Binder 通信有很大的帮助。在学习 Binder 通信之前,我们要问自己,为什么需要 Binder, 它究竟是怎么实现的?

其实我们费这么大的劲还是为了进程间通信,这里举个不是很恰当的例子。 有 A 、B 两个小岛,中间是分隔开的,A 岛上的人想吃 B 岛上特有的水果,怎么办? 这里就得有很多分工,还得有工具,下面我们开始展开。 这里很重要的一点是,A 岛要拿到 B 岛上一模一样的水果,同一个对象,不要仿制品。 这个很重要,就像我们吃海鲜,要吃从日本,啊不......打包好的海鲜。讲究的是同一个对象。

A 岛上根据水果店的地址去买就好了,但是要是给了 B 岛上的水果店地址,A 岛具名就没有那么简单买到了。 这里我们需要折腾一下。

同一个进程间的对象传递都是通过引用来做的,因而本质上就是传递了一个内存地址。这种方式在跨进程的情况下就无能为力了,由于采用虚拟内存机制,两个进程中都有自己独立的内存地址空间,所以跨进程传递的地址值是无效的。(那咋办呢?)

Parcel 具有打包和重组的能力。这里可以把它想象成一个集装箱。

  1. Parcel 设置相关

    dataSize( ) : 获取当前已经存储的数据大小

    dataPosition( ): 数据的当前位置值,有点类似于游标

  2. Primitives

    原始类型数据的读写操作, 用哪种方式写入的数据就要相应的方式正确读取。

    数据是按照 host cpu 的字节序来读写的。

    writeByte(byte): 写入一个 byte

    readByte( ): 读取一个 byte

  3. Primitive Arrays

原始数据类型数组的读写操作通常是先写入用 4 个字节表示的数据大小值,接着再写入数据本身。

注意: 如果写入数据时系统已经超出了 Parcel 的存储能力,它会自动申请所需的内存空间,并扩展 dataCapacity, 而且每次写入都是从 dataPosition() 开始的。

  1. Parcelables

    遵循 Parcelables 协议的对象可以通过 Parcel 来存取

  2. Bundles

    Bundle 继承自 Parcelable ,是一种特殊的 type-safe 的容器。

    最大的特点是采用键值对的方式来存取数据。

  3. Active Objects

    写入的是它们的特殊标志引用。在 Parcel 中读取这些对象时,大家看到的并不是重新创建的对象实例,而是原来那个被写入的实例。(这个就非常神奇了)

以这种方式传输的对象主要有2类:

  • Binder

android 系统核心的 IPC 通信机制,同时 也是一个对象。

利用 Parcel 将 Binder 对象写入,读取时就能得到原始的 Binder 对象,或者是它的特殊代理实现(最终操作的还是原始 Binder 对象)

writeStrongBinder(IBinder)

writeStrongInterface(IInterface)

readStrongBinder()

......

  • FileDescriptor

是 Linux 中的文件描述符,可以通过 Parcel 的如下方法进行传递。

writeFileDescriptor(FileDescriptor)

readFileDescriptor()

因为传递后的对象仍然会基于和原对象相同的文件流进行操作,所以可以认为是 Active Object 的一种。

  1. Untyped Containers

用于读写标准的任意类型的 java 容器。

writeArray(Object[])

readArray(ClassLoader)

writeList(List)

readList(List, ClassLoader)

......

这里很厉害的地方就是,Parcel 能够根据协议(打包和重组所用的协议必须是配套的)来为接收方提供完整还原出原始数据对象的业务。

这里流程比较复杂,但是其实原理还是为了跨进程通信,Parcel 有这样的能力。