huazi

huazi

《Android开发艺术探索》笔记-第2章

IPC 是 Inter-Process Communication 的缩写,含义为进程间通信或者跨进程通信,是指两个进程间进行通信的过程。

线程是 CPU 调度的最小单元,同时线程是一种有限的系统资源。进程一般是指一个执行单元,一个进程可以包含多个线程。一个进程可以只有一个线程,即主线程。
多进程分为两种,第一种情况是一个应用因为某些原因自身需要采用多线程模式来实现,另一种情况是当前应用需要向其他应用获取数据。

Android 中使用多进程只有一种方法,就是给四大组件在 AndroidMenifest 中指定 android 属性,除此之外别无他法。adb shell ps 命令可以查看进程信息。

进程名以 : 开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程;
进程名不以 : 开头的进程属于全局进程,其他应用通过 ShareUID 方式可以和它跑在同一个进程中。系统会为每个应用配备唯一的 UID, 具有相同 UID 的应用才能共享数据。
Android 中每个进程分配了独立的虚拟机,每个虚拟机分配不同的地址空间。运行在不在进程中的四大组件,他们之间想通过内存共享数据,都会共享失败。

多进程会造成如下几个问题:

  1. 静态成员和单例模式完全失效。
  2. 线程同步机制完全失效。
  3. SharePreferences 的可靠性下降。
  4. Application 会多次创建。

对于 1,2 是同一个问题,不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象。

SharePreferences 不支持两个进程同时进行写操作,因为 SharePreferences 底层是通过读写 xml 文件实现的,并发读写都有可能出现问题。

运行在不同进程中的组件属于两个不同的虚拟机和 Application。每启动一个进程就会走一遍 Application onCreate。

IPC 基础概念#

IPC 基本概念主要包括三方面,Serializable 接口,Parcelable 接口,以及 Binder,Serializable 和 Parcelable 可以完成对象的序列化过程,Intent 和 Binder 传输数据时就需要使用 Serializable 和 Parcelable。

Serializable 接口#

Serializable 是 Java 中提供的序列化接口,为对象提供标准的序列化和反序列化操作。
serialVersionUID 是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的 serialVersionUID 只有和当前类的 serialVersionUID 相同才能够正常的被反序列化。序列化的时候,系统会把当前类的 serialVersionUID 写入序列化的文件中,当反序列化的时候系统去检测文件中的 serialVersionUID 是否和当前类的 serialVersionUID 相同,一致则正常反序列化,否则无法正常发序列化。一般来说需要手动指定 serialVersionUID。如果不指定,当前类有所改变会重新计算当前类的 hash 值并把它赋值给 serialVersionUID

  1. 静态成员变量属于类,不属于对象,所以不会参与序列化过程。
  2. transient 标记的变量不参与序列化的过程。

Parcelable#

Android 提供的新的序列化方式:Parcelable,也是一个接口。序列化功能由 writeToParcel 完成。Intent,Bundle,Bitmap 都实现了此接口,可以直接序列化。

Serializable 是 Java 接口,使用起来简单但开销很大,序列化和反序列化需要大量 I/O 操作。

Parcelable 使用麻烦,效率高,推荐使用。

将序列化对象存储到设备中或将对象序列化后通过网络传输,推荐使用 Serialzalble。

Binder#

Android 开发中 Binder 主要用在 Service,AIDL 和 Messenger 中,普通的 Service 中的 Binder 不涉及进程间通信。Messenger 底层是 AIDL。Binder 的唯一标识,一般用当前的 Binder 类名表示。

Binder 是 ServiceManager 连接各种 Manager (ActivityManager, WindowManager, 等) 和相应 ManagerService 的桥梁。

系统生成的 Binder 类参数解析:

  • DESCRIPTOR, Binder 的唯一表示,一般用当前 Binder 的类名表示
  • asInterface (android.os.IBinder obj), 用于将服务端的 Binder 对象转成客户端所需的 AIDL 接口类型的对象,这种转换是分进程的,如果客户端,服务端位于同一进程,那么此方法返回的就是服务端的 Stub 对象本身,否则返回系统封装后的 Stub.proxy 对象。
  • adBinder, 返回当前 Binder 对象
  • onTransact, 运行在服务端的 Binder 线程池中,当客户端发起请求时,远程请求会通过底层封装后交由此方法来处理。

Binder 工作机制还需注意两点:

  1. 当客户端发起远程请求时,当前线程会被挂起直到服务端进程返回数据,是个耗时操作。
  2. 服务端 Binder 方法运行在 Binder 线程池中所以 Binder 方法不管是否耗时都应该采用同步方式去实现。

Binder 的工作机制图:
Binder 工作流程.png

Binder 运行在服务端,如果服务端由于某中原因导致进程终止,会导致远程调用失败,Binder 提供了两个方法 linkToDeathunLinkToDeath 可以设置 Binder 的死亡代理。

通过 Binder 的方法 isBinderAlive 也可以判断 Binder 是否死亡。

Android 中的 IPC 方式#

  1. Bundle
    Bundle 实现了 Parcelable 接口,所以方便的在进程中传输。

  2. 使用文件共享
    两个进程通过读写同一个文件交换数据。并发读写会有问题,尽量避免并发写的情况,考虑使用线程同步来限制多个线程的写操作。
    SharePrefrences 也属于文件的一种,系统对他的读写有缓存策略,多进程模式下,读写不可靠。

  3. 使用 Messenger
    Messenger 是一种轻量级的 IPC,底层实现是 AIDL。Messenger 以串行的方式处理客户端的消息。主要是为了传递消息。
    工作原理图:
    messenger 工作原理.png

  4. 使用 AIDL
    使用 AIDL 进行进程间通信分为服务端和客户端两方面,大致流程:服务端首先创建 Service 用来监听客户端请求,然后创建 AIDL 文件将暴露给客户端的接口在这个 AIDL 中声明,随后在 Service 中实现这个 AIDL 接口。客户端绑定服务端的 Service,绑定成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,接着调用 AIDL 方法。

  5. 使用 ContentProvider
    Android 中专门为不同应用间进行数据共享的方式,底层实现是 Binder。ContentProvider 对底层数据存储没有要求。

首先需要注册 Provider,其中 android:authorities 是 Provider 的唯一标识,通过这个就可以访问 Provider。

Provider 的 onCreate 运行在主线程,其他方法运行在 Binder 线程池中。query,update,insert,delete 是存在多线程并发,因此方法内部做好线程同步。

  1. 使用 Socket

Socket 是网络通信的概念。

  • TCP 面向连接协议,提供稳定的双向通信功能。
  • UDP 无连接协议,提供不稳定的单向通信功能,也可以提供双向通信,有更好的效率,缺点是不能保证正确传输。

Binder 连接池#

使用 AIDL 大致流程:首先创建一个 Service 和 AIDL 接口,接着创建一个继承自 AIDL 接口中的 Stub 类并实现 Stub 中的抽象方法,在 Service 的 onBinder 方法中返回这个类的对象,然后再客户端就可以绑定服务端的 Service,建立连接后就可以远程访问服务端的方法了。

当项目规模很大的时候,创建很多个 Service 是不对的做法,因 Service 是系统资源,太多的 Service 会使得应用看起来很重,所以最好是将所有的 AIDL 放在同一个 Service 中去管理。

Binder 连接池的作用是将每个业务模块的 Binder 请求统一转发的远程 Service 中去执行,避免了重复创建 Service 的过程。

BinderPool 极大的提高 AIDL 的开发效率,避免大量创建 Service,建议在 AIDL 中引入 BinderPool。

选择合适的 IPC 方式#

名称优点缺点适用场景
Bundle简单易用只能传输 Bundle 支持的数据类型自大组件间的进程通信
文件共享简单易用不适合高并发场景,无法做到进程间及时通信无并发访问情形,交换简单的数据实时性不高的场景
AIDL功能强大,支持一对多并发通信,支持实时通信使用稍复杂,需要处理好线程同步一对多通信有 RPC 需求
Messenger功能一般,支持一对多串行通信,支持实时通信不能很好的处理高并发情形,不支持 RPC,数据通过 message 传输,只能传输 Bundle 支持的数据类型低并发的一对多即时通信,无 RPC 需求,或者无需发挥结果的 RPC 需求
ContentProvider在数据源访问方面功能强大,支持一对多并发数据共享,可以通过 Call 方法扩展可以理解为受约束的 AIDL,主要提供数据源的 CRUD一对多的进程间数据共享
Socket功能强大,可以通过网络传输字节流,支持一对多并发实时通信实现细节稍微繁琐,不支持直接 RPC网络数据交换
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。