前言
提到 Android 进程间的通信方式,即使是 Android 客户端开发初学者,也能列举出来几种,无外乎:
- bundle
- 文件共享
- AIDL(Binder)
- Messenger
- ContentProvider
- Socket
然而都2022年了,本文如果只是介绍下以上的几种进程间通信的方式,就没什么意义了,也太对不起观众了,同时以上几种方式,也不能满足题目的需求:大数据,高效的跨进程传输。
有些同学可能会提到另外一种方式:共享内存(MemoryFile & SharedMemory),这种方式的确是可以满足题目的需求,不过共享内存的使用不是很简单,没有进程间同步机制,这个需要使用者自行处理,这样就加大了该方式的使用难度,下文会详细说明下用共享内存进行通信的难点。
1 使用场景
在介绍这种通信方式之前,先看下为什么需要进行跨进程的大数据的高效传输,有哪些场景需要进行跨进程的数据传输。
对于大部分的 app 开发同学,一般应用都是单进程的模式,并不需要进行跨进程的数据通信,即使有多进程的场景,一般数据量不会特别大,也不是持续性的,频繁性的。
那么在 Android 系统中,哪些数据是大量的,需要跨进程传递的,对 Android 图像系统比较了解的同学会想到屏幕上渲染的数据,对多媒体比较了解的同学会想到音视频数据,这些数据都有类似的特点:大数据量(高分辨率),持续性(高采样率,高刷新率)。
此处以一路30帧的720p的 camera NV21数据为例,1秒钟的数据量为:1280 * 720 * 3 / 2 * 30 = 41472000Byte = 39.5MB,对于这个数量级的数据,一次内存 copy 对系统资源都是很大的损耗,这也证明了为什么前面介绍的方式1,2,4,5,6的通信方式不适合大数据的传输,一种原因就是因为他们需要进行多次的内存 copy 操作,效率较低。方式3:AIDL(Binder)虽然只有一次 copy 操作,但是 Binder 对单次通信数据量有大小限制(默认< 1Mb),同时由于很多其他通信操作是共享Binder内存的,如果Binder通信过于频繁,是会拖慢应用的响应时间。
而方式7:共享内存理论是可以满足这个需求,不过我们来看下,如果要基于共享内存来实现数据的传输需要完成哪些事情。
2 解决方案
假设目前有两个进程:进程A,进程B,进程A和进程B之间已经建立好了一块共享内存,两个进程都可以对该内存区域进行访问。目前进程A需要向进程B持续的传输大量数据,那么需要哪些步骤呢?
2.1 设计思路

- step 1:进程A向共享内存写入一段数据。
- step 2:进程B读取这段数据。
- 进程A重复 step 1:再向共享内存写入一段数据。
以上1-2-1-2循环,这样就可以了吗?当然不会这么简单,这里面有一些同步的问题:
- 进程A写入后,如何通知进程B读取。
- 在进程B没有取走之前,进程A如果有新数据生成,怎么办?
- 进程B取走数据后,如何通知进程A继续写入?
对于问题1,2,进程A需要完成写入后触发 step3 通知进程B可以读取,进程B完成读取后,触发 step4 通知进程A可以继续写入。

如果解决以上问题后,我们会发现其实已经实现了一个基础的生产者消费者模型。(对于问题2,又可以扩展出缓存,ping-pong buffer,3-buffer等等)。
而实现以上这套模型的成本应该说还是很高的,但是理论上完全可行,不过为了让事情更简单,是否有更简单的方法呢,是否有这样一个组件,在进程A和进程B直接建立一个管道,进程A只管写,写不下了,阻塞或者返回出错,有空间可以继续写了,通知进程A继续进行写,进程B只管读,读不到,阻塞或者返回出错,有数据了,通知进程B继续读,由这个管道处理同步通知这些事情,linux下有提供 pipe 这种通信方式,不过pipe需要多次内存 copy,也不适合大数据的传输,且 Android 系统并没有在应用层暴露这个 pipe 的接口。

对于 Android 系统,渲染,音视频等模块比较了解的同学应该会想到 Android 系统里面的 BufferQueue,那么先了解下 BufferQueue。
2.2 BufferQueue
对业务开发来说,无法接触到 BufferQueue,甚至不知道 BufferQueue 是什么东西。对系统来说,BufferQueue 是很重要的传递数据的组件,Android 显示系统依赖于 BufferQueue,只要显示内容到“屏幕”(此处指抽象的屏幕,有时候还可以包含编码器),就一定需要用到 BufferQueue,可以说在显示/播放器相关的领域中,BufferQueue 无处不在。即使直接调用 Opengl ES 来绘制,底层依然需要 BufferQueue 才能显示到屏幕上。
BufferQueue 是 Android 显示系统的核心,它的设计思想是生产者-消费者模型,只要往 BufferQueue 中填充数据,则认为是生产者,只要从 BufferQueue 中获取数据,则认为是消费者。有时候同一个类,在不同的场景下既可能是生产者也有可能是消费者。如 SurfaceFlinger,在合成并显示 UI 内容时,UI 元素作为生产者生产内容,SurfaceFlinger 作为消费者消费这些内容。而在截屏时,SurfaceFlinger 又作为生产者将当前合成显示的 UI 内容填充到另一个 BufferQueue,截屏应用此时作为消费者从 BufferQueue 中获取数据并生产截图。

同时使用 BufferQueue 的生产者和消费者往往处在不同的进程,B

本文介绍了一种基于ImageReader和ImageWriter实现的Android跨进程大数据高效传输方法,适用于Android系统下的智能设备开发,尤其是对Camera和图形渲染有特殊需求的应用。
最低0.47元/天 解锁文章
1467

被折叠的 条评论
为什么被折叠?



