编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 02.Android虚拟机知识
  • 03.App核心概念分析
  • 04.Android消息机制
  • 05.Android程序UI框架
  • 06.Android应用窗口
  • 07.WMS机制深入分析
  • 08.PMS机制深入分析
  • 09.AMS机制深入分析
  • 11.四大组件原理分析
  • 12.四大组件原理分析
  • 13.View绘制流程分析
  • 14.View渲染流程分析
  • 16.Android绘制原理
  • 17.Android事件传输
  • 18.Android事件拦截
  • 21.View自定义控件反思
  • 22.ViewGroup自定义控件
  • 25.IPC跨进程通信实践
  • 29.Binder机制的理解

25.IPC跨进程通信实践

目录介绍

  • 01.整体概述
    • 1.1 项目背景
    • 1.2 遇到问题
    • 1.3 基础概念
    • 1.4 设计目标
  • 02.IPC通信方式
    • 2.1 使用Intent
    • 2.2 使用文件共享
    • 2.3 使用Messenger
    • 2.4 使用AIDL
    • 2.5 使用ContentProvider
    • 2.6 使用Socket
  • 03.跨进程要点
    • 3.1 如何开启多进程
    • 3.2 数据序列化
    • 3.7 方案的对比选择
  • 05.方案基础设计
    • 5.1 整体架构图
    • 5.2 UML设计图
    • 5.3 关键流程图
    • 5.4 接口设计图
    • 5.5 模块间依赖关系
  • 06.其他设计说明
    • 6.1 性能设计
    • 6.2 稳定性设计
    • 6.3 灰度设计
    • 6.4 降级设计
    • 6.5 异常设计

01.整体概述

1.1 项目背景

  • 进程之间如何通信

1.2 遇到问题

1.3 基础概念

  • IPC(Inter-Process Communication)
    • 含义即为进程间通信或者翻译为跨进程通信,是指两个进程之间进行数据交换的过程。
  • 一般情况下,在 Android系统中一个应用就只享有一个进程
    • 在最简单的情况下一个进程可以只包含有一个线程(当然,一般情况下是不可能的),即主线程,也称为 UI 线程
  • 有时候应用因为某些原因需要采用多进程模式
    • 此时如果要在应用内的不同进程间进行通信,就需要使用到 IPC 机制。或者是两个不同的应用需要进行数据交换,此时也一样需要依靠 Android 系统提供的 IPC 方案。

1.4 设计目标

02.IPC通信方式

  • 使用Intent
    • Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
  • 使用文件共享
    • 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)
  • 使用Messenger
    • Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。
  • 使用AIDL
    • 服务端,创建Service用于监听客户端的连接请求,然后创建一个AIDL接口文件,里面是将要实现的方法,注意这个方法是暴露给客户端的的,在Service中实现这个AIDL接口即可。
    • 客户端,先绑定服务端Service,绑定成功后,将服务端返回的Binder对象转换成AIDL接口所属的类型,最后调用AIDL的方法就可以了。
  • 使用ContentProvider
    • 许多如通讯录、公共相册、日程表,需要跨进程访问。使用方法:继承 ContentProvider 类实现抽象方法,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。
    • ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。

2.1 使用Intent

  • 使用介绍
    • Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
  • 举个使用场景例子
    • App通过intent调用打电话;App获取手机存储中图片资源;App监听手机屏幕亮屏和灭屏事件;App分享文件到QQ;等等,都是属于跨进程通信。

2.2 使用文件共享

  • 使用介绍
    • Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
    • 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同)。SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。
  • 举个使用场景例子
    • 待完善

2.3 使用Messenger

  • Messenger 是一种轻量级的 IPC 方案
    • 它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。
  • 服务端进程:
    • 服务端创建一个 Service 来处理客户端请求,同时通过一个 Handler 对象来实例化一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。
  • 客户端进程:
    • 首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了,消息类型是 Message 。如果需要服务端响应,则需要创建一个 Handler 并通过它来创建一个 Messenger(和服务端一样),并通过 Message 的 replyTo 参数传递给服务端。服务端通过 Message 的 replyTo 参数就可以回应客户端了。
  • 注意: 客户端和服务端是通过拿到对方的 Messenger 来发送 Message 的。
    • 客户端通过 bindService onServiceConnected 而服务端通过 message.replyTo 来获得对方的 Messenger 。
    • Messenger中有一个Handler以串行的方式处理队列中的消息。不存在并发执行,因此我们不用考虑线程同步的问题。

2.4 使用AIDL

  • AIDL 是什么
    • AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务端和客户端通信接口的一种描述语言,可以拿来生成用于 IPC 的代码。从某种意义上说 AIDL 其实是一个模板,因为在使用过程中,实际起作用的并不是 AIDL 文件,而是据此而生成的一个 IInterface 的实例代码,AIDL 其实是为了避免我们重复编写代码而出现的一个模板
    • Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用Messenger,而且Messenger只适合传递消息,不能跨进程调用服务端的方法。
    • AIDL 可以解决并发和跨进程调用方法的问题,要知道Messenger本质上也是AIDL,只不过系统做了封装方便上层的调用而已。
  • AIDL文件支持的数据类型
    • 基本数据类型,比如int,long,double;String 和 CharSequence
    • ArrayList ,里面的元素必须能够被 AIDL 支持;HashMap ,里面的元素必须能够被 AIDL 支持;
    • Parcelable ,实现 Parcelable 接口的对象,注意:如果 AIDL 文件中用到了自定义的 Parcelable 对象,必须新建一个和它同名的 AIDL 文件。
    • AIDL ,AIDL 接口本身也可以在 AIDL 文件中使用。
  • 如何使用
    • 服务端创建一个 Service 用来监听客户端的连接请求,然后创建一个 AIDL 文件,将暴露给客户端的接口在这个 AIDL 文件中声明,最后在 Service 中实现这个 AIDL 接口即可。
    • 绑定服务端的 Service ,绑定成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,然后就可以调用 AIDL 中的方法了。
    • 户端调用远程服务的方法,被调用的方法运行在服务端的 Binder 线程池中,同时客户端的线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,导致 ANR 。
    • 客户端的 onServiceConnected 和 onServiceDisconnected 方法都在 UI 线程中。
  • 服务端
    • 步骤1:新建定义AIDL文件【特别注意包名保持一致】,并声明该服务需要向客户端提供的接口。补充,如果aidl中有对象,则需要创建对象,并且实现Parcelable
    • 步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法
    • 步骤3:在AndroidManifest.xml中注册服务 & 声明为远程服务
  • 客户端
    • 步骤1:拷贝服务端的AIDL文件到目录下【需要把service端所有的aidl都拷贝到client来】
    • 步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
    • 步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service
  • 遇到一些问题
    • aidl接口是如何定义?
    • 定义后aidl接口如何编译生成.java文件?
    • aidl通信是单向还是双向?单向通信,客户端调
    • 其他注意点:注意的是需要先启动service端的apk,在启动client端的apk
  • 不同的场景
    • 场景一、service 和 client 在同一个 project 中。service项目可以直接依赖client项目
    • 场景二、service 和 client 分属于不同的 project 中。将service项目中的 aidl 文件夹原封不动的拷贝到client项目中的对应位置

2.5 使用ContentProvider

  • 用于不同应用间数据共享,和 Messenger 底层实现同样是 Binder 和 AIDL,系统做了封装,使用简单。
  • 系统预置了许多 ContentProvider ,如通讯录、日程表,图片等,需要跨进程访问。
    • 使用方法:继承 ContentProvider 类实现 6 个抽象方法,这六个方法均运行在 ContentProvider 进程中,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。

2.6 使用Socket

  • Socket起源于 Unix,而 Unix 基本哲学之一就是“一切皆文件”
    • 都可以用“打开 open –读写 write/read –关闭 close ”模式来操作。Socket 就是该模式的一个实现,网络的 Socket 数据传输是一种特殊的 I/O,Socket 也是一种文件描述符。Socket 也具有一个类似于打开文件的函数调用: Socket(),该函数返回一个整型的Socket 描述符,随后的连接建立、数据传输等操作都是通过该 Socket 实现的。
  • 常用的 Socket 类型有两种:
    • 流式 Socket(SOCK_STREAM)和数据报式 Socket(SOCK_DRAM)。流式是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用;数据报式 Socket 是一种无连接的 Socket ,对应于无连接的 UDP 服务应用。Socket 本身可以传输任意字节流。
  • Socket 是连接应用层与传输层之间接口(API)。
    • 网络模型
      网络模型

03.跨进程要点

3.1 如何开启多进程

  • 为一个 Android 应用开启多进程模式的方法有两种。
    • 第一种方法是在 AndroidManifest 中为四大组件指定 android:process 属性,为其声明要在哪个进程名下运行,即可开启多进程。
    • 第二种方法是通过 JNI 在 native 层中 fork 一个新的进程。这里只讨论第一种方法。
  • Android 应用默认在命名为包名的进程下运行
    • 除非你为其指定了 android:process 属性
  • 虽然开启多进程的方法并不算麻烦,但当应用开启了多进程后,其实会对来很多的负面影响,主要有以下几个:
    • 静态成员与单例模式失效
    • 线程同步机制失效
    • SharedPreferences 可靠性下降
    • Application 被创建多次
  • 为了解决多进程带来的问题
    • 系统也为开发者提供了很多的跨进程通信方式,比如文件共享、ContentProvider、Messenger、AIDL、Socket 等

3.2 数据序列化

  • 必须对数据序列化
    • 跨进程通信的目的就是为了进行数据交换,但并不是所有的数据类型都能被传递,除了基本数据类型外,还必须是实现了序列化和反序列化的数据类型才可以,即实现了 Serializable 接口或 Parcelable 接口的数据类型

07.进程间通信问题

  • 程序世界中,存在着大量的 通信 场景。搜索我们的知识,解决 进程间通信 问题有以下几种方式:
  • 管道
    • 普通管道pipe:一种 半双工 的通信方式,数据只能 单向流动 ,而且只能在具有 亲缘关系 的进程间使用。
    • 命令流管道s_pipe: 全双工,可以同时双向传输
    • 命名管道FIFO:半双工 的通信方式,允许 在 无亲缘关系 的进程间通信。
  • 消息队列 MessageQueue:
    • 消息的链表,存放在内核 中 并由 消息队列标识符 标识。
    • 消息队列克服了 信号传递信息少、管道 只能承载 无格式字节流 以及 缓冲区大小受限 等缺点。
  • 共享存储 SharedMemory:
    • 映射一段 能被其他进程所访问 的内存,这段共享内存由 一个进程创建,但 多个进程都可以访问。
    • 共享内存是 最快的 IPC 方式,它是针对 其他 进程间通信方式 运行效率低 而专门设计的。
    • 往往与其他通信机制一同使用,如信号量配合使用,来实现进程间的同步和通信。
  • 信号量 Semaphore:
    • 是一个 计数器 ,可以用来控制多个进程对共享资源的访问。它常作为一种 锁机制,防止某进程正在访问共享资源时,
    • 其他进程也访问该资源,实现 资源的进程独占。因此,主要作为 进程间 以及 同一进程内线程间 的同步手段。
  • 套接字 Socket:
    • 与其他通信机制不同的是,它可以 通过网络,在不同机器之间进行进程通信。
  • 信号 signal:
    • 用于通知接收进程 某事件已发生。机制比较复杂。

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
贡献者: yangchong211
上一篇
22.ViewGroup自定义控件
下一篇
29.Binder机制的理解