背景
之前我在文章里说过,我们的录像模块是跨进程的.为什么要这样设计,是因为录像这个过程是一个长期执行的过程,中间不能有断开.为了保证稳定性,通过进程隔离将录像业务同主业务分离.
通常情况下,如果在录像过程中APP崩溃了,就会导致录像文件损坏,可能前面录的数据就丢了.
但是如果做了进程隔离的话,主进程发生异常崩溃,不影响录像进程.而录像进程功能相对单一简单,不会导致崩溃.当主进程崩溃时,录像进程可以检测到,这时及时停止当前的录像,录像依然可以正常结束,就可以播放.
实现
安卓端实现进程隔离是相对容易的,大体上为:
- 创建一个
Service
- 在
Manifest
里面给这个Service
配置process
属性,表示该服务将运行在独立进程,其进程ID
就是process
的值.比如:<service android:name=".logic.impl.FFMuxerService" android:process="com.android.mpu.ff" />
- 主进程启动通过
startService
启动这个服务,并且通过bindService
与服务绑定,这样子进程也就启动了. - 主进程通过
IPC
(binder
)与子进程进行数据交互.交互过程主要有:- 开始录像
- 录像采集/编码视频帧传递给录像进程
- 停止录像.
其跨进程的架构图如下所示:
问题以及解决方案
实际实现过程中又不可避免地带来一些问题,主要有:
- 跨进程
Binder
大数据量传输限制(TransactionTooLargeException
).安卓内部对于Binder
的数据传输做了限制,最多不超过1M,但是媒体帧数据量通常比较大,有可能超过了1M.这样通过Binder
传递的话,效率不高,而且还容易出现TransactionTooLargeException
异常.
可通过共享内存来解决这个问题.安卓平台有个MemoryFile
可以用来共享内存,相比直接传输媒体帧,主进程可创建一个MemoryFile
,并将其fd
通过binder
传递给子进程,子进程得到fd
后,取出内存数据.这样可以有效避免大数据量传输的问题,且效率也更高.
MemoryFile
相关知识点,可以参考这篇文章的介绍:MemoryFile
主进程侧关键代码片段如下:// 把内存写入MemoryFile对象 mf.writeBytes(buffer, 0, 0, info.size) param.frameType = AVMEDIA_TYPE_VIDEO param.size = info.size // 取得MemoryFile对应的FileDescriptor param.pfd = muxCon.getFD