开发艺术探索笔记

开发艺术之IPC

2019-12-17  本文已影响0人  请叫我林锋
一、如何开启多进程模式

我们可以给四大组件在 AndroidManifest 中指定 android:process 属性来开启多进程,默认进程的进程名就是包名,假设现在包名为 "com.example.myapplication"

通过 adb shell ps 或者 adb shell ps | grep 包名 来查看进程信息


二、多进程带来的问题以及问题出现的原因

每一个进程都拥有一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,不同虚拟机访问一个类的对象就会产生多份副本。这位造成下面几个问题:


三、序列化的使用

序列化表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。可以通过实现 Serializable 接口或者 Parcelable 接口来实现序列化。

Serializable 接口和 Parcelable 接口的比较:

序列化接口比较.png
serialVersionUID

有两种变量不参与序列化

推荐阅读:Serializable 这么牛逼,Parcelable 要你何用?


四、Binder 通信机制

a. 概念:

b. Android 是基于 Linux 内核基础上设计的,Linux 已经拥有了 socket、管道、消息队列、共享内存,为什么还需要 Binder?

IPC方式 数据拷贝次数
共享内存 0
Binder 1
Socket/管道/消息队列 2

对于 Socket、管道、消息队列来说,数据先从发送方缓存区拷贝到内核缓存区中,然后再从内核缓存区拷贝到接收方缓存区,一共两次拷贝,如图:

传统 IPC 通信原理

而对于 Binder 来说,数据从发送方缓存区拷贝到内核开辟的缓存区中,内核缓存区内核中数据接收缓存区之间的映射关系,节省了一次数据拷贝,如图:

Binder 通信原理

共享内存虽然无需拷贝,但控制复杂,难以使用,综合来看 Binder 最具优势。

c. Binder 通信模型

Binder 框架定义了四个角色:Server,Client,ServiceManager以及Binder驱动。

其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间;其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。关系如图:


Binder 通信模型关系

Binder 通信过程如下:


Binder 通信过程

d. 代理模式Proxy

在数据流经 Binder 驱动的时候驱动会对数据做一层转换,当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。如图:

image.png

参考文章:写给 Android 应用工程师的 Binder 原理剖析Android Bander设计与实现 - 设计篇


五、Android 中的 IPC 方式

a. Bundle:

由于 Bundle 实现了 Parcelabe 接口,所以它可以在不同进程间传输。可以在 Bundle 中附加我们要传输的数据,然后通过 Intent 发送出去。

我们传输的数据必须能够被序列化,比如基本类型、实现了 Parcelabe 接口或者 Serializable 接口的对象。

思考一个特殊场景,若 A 进程需要完成一个计算,并在结束后启动 B 进程的一个组件并传递结果给 B 进程,但这个结果不能放入 Bundle。此时可以通过 Intent 启动 B 进程的 Service,让它在后台计算,从而避免进程间通信。


b. 文件共享:

通过文件共享,可以交换一些文本信息,还可以序列化一个对象到文件系统,同时从另一个进程中恢复这个对象。

缺点:多进程并发读/写,读出的内容可能不是最新的;并发写更可能会产生冲突

适用场景:对数据同步要求不高的进程之间通信,并妥善处理并发读/写的问题

注意 SharedPreferences 是个特例,虽然它的本质也是文件的一种,但系统对它的读/写有一定的缓存策略,即内存中会有一份 SharedPreferences 的缓存,所以在多进程模式下,它的读/写就会变得不可靠。因此,不要在多进程中使用 SharedPreferences。


c. Messenger

概念:

实现方法:

Messenger 流程图

缺点:**


d. AIDL

基本使用:

使用观察者模式,客户端监听服务端数据变化:

注意:因为 Binder 传输对象是通过序列化和反序列,所以客户端在注册和解注册传到服务端的 listener 不是同一个对象,导致服务端找不到注册时的 listener 最终解注册失败。

解决办法:使用 RemoteCallbackList 来删除跨进程的 listener 接口。虽然传到服务端的 listener 不是同一个,但是它们的底层 Binder 对象是同一个,RemoteCallbackList 能够遍历 Map 通过 Binder 对象找到注册时的那个 listener 从而解注册。而且它还能够在客户端进程终止后,自动移除客户端注册的 listener。

AIDL 文件支持的数据类型:

注意:

  1. 自定义的 Parcelable 对象和 AIDL 对象需要显式 import 到当前的 AIDL 文件中
  2. 如果 AIDL 文件用到了 Parcelable 对象,那么需要新建一个同名的 AIDL 文件并在其中声明它为 Parcelable 类型
  3. AIDL 中除了基本数据类型,其他参数必须加上方向:in、out 或者 inout
  4. CopyOnWriteArrayList,支持并发读/写,会在 Binder 中形成 ArrayList 传递给客户端
可能产生 ANR 的场景:

总结:不要在主线程调用另一端的耗时方法


e. ContentProvider

ContentProvider 是 Android 中提供的专门用于不同应用进行数据共享的方式,和 Messenger 一样,ContentProvider 的底层实现同样也是 Binder。

注意:


f. Socket

Socket 也称为“套接字”,不仅可以实现进程间通信,还可以实现设备间通信,两种形式:

使用 Socket 来进行通信,要注意两点:

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 

六、Binder 连接池的概念

a. 背景:按照 AIDL 的流程,如果有 100 个业务模块使用 AIDL 来进行进程间通信,就需要创建 100 个 Service,然后在 100 个 Service 的 onBind 方法返回对应的 binder 对象。

Service 是四大组件之一,这么多的 Service 会浪费大量的系统资源,所以需要减少 Service 的数量,将所有的 AIDL 放在同一个 Service 中去管理。

b. 作用:将每个业务模块的 Binder 请求统一转发到远程 Service 中去执行,从而避免了重复创建 Service 的过程,工作原理如图:


Binder 连接池工作原理

c. 实现:


七、选用合适的 IPC 方式
选用合适的 IPC 方式
上一篇下一篇

猜你喜欢

热点阅读