前言
2016年即将过去,在这一时刻总觉得应该写点什么有价值的东西来纪念一下。思前想后,觉得脱离M$体系转向Android/Linux的开发有两年时间了,在这一过程中还是有很多收获的:澄清了一些模糊概念、验证并确认了一些个人做法、领会了开源项目中的精妙内容。由于Android体系的庞大代码量,当初为尽快入门采用的是“提纲挈领、重点突破、由线及面”的策略,在了解Android的整体设计哲学思想(详见引用一,别看是2010年的文章但没有之一只有唯一)前提下深入学习其重要且极具代表性的Camera和MediaRecorder子系统,很好地达到了预期目的。
做为移动设备服务而推出的Android,录像是其主要功能。而录像功能使用了Camera子系统启动摄像头获取原始数据、使用MediaRecorder子系统启动软或硬编码器进行编码。两大子系统涉及的服务CameraService和MediaPlayerService均以mediaserver进程做为运行平台。
在Camera和MediaRecorder子系统实现中,借鉴了大量的面向对象思想:如设计原则中的接口隔离原则,设计模式中的代理(委托)模式、适配(包装)模式、建造者模式,类关系中的继承、组合、依赖,等等。以下将从Framework层开始(不包括JNI)往下分析录像的实现。
一、Camera子系统分析
以下是GingerBread(2.3)的Camera子系统框图,之后的版本尽管在具体代码实现上做了很多调整,但是其核心实现思想是大同小异的:即面向接口实现、Binder方式双向访问、HAL隐藏硬件相关信息。
分析:
上面的框架图是基于接口实现分类角度的初步分析,下面将从接口运行载体(应用/服务)角度进一步分析(注:红色双向箭头仅表明app和service间的存在RPC逻辑联系,类依赖关系也仅是表明binder对象间逻辑联系,实际的关联在上面的框图中体现):
分析:
CameraService::Client做为CameraService的内部类,负责调用CameraHardwareInterface来实现硬件操作。此处利用了面向对象的多态性来适配不同版本的HAL:CameraClient(对应操作V1.0版HAL)和Camera2Client(对应操作V2.0、V3.0版HAL)均是CameraService::Client的子类,父类是虚类、实例化的是子类。在上面的框图中由于版面限制仅列举了CameraClient。
参照常规的网络模型进行类比,来理解Binder方式RPC通讯的使用原理:
关于binder proxy/binder native的使用规则涉及Bp和Bn两个概念,其英文解释是:
“n” is native, that is the class you inherit from to implement the interface; “p” is proxy, that is the class that is created to perform interface calls through IPC.
主要意思就是“binder proxy和binder native对象均继承自同一抽象接口,binder native对象在RPC通讯响应端(显式)实例化,以抽象接口的方式反馈给RPC发起端(隐式)创建binder proxy对象实例,但最终是以抽象接口的形式供RPC发起端使用”。在这一点上,和传统的“client/server”相比是有差异的。
强调一点,上面所有的分析都是基于 Camera API v1进行,Camera API v2与之有较大的不同,在未做深入了解前先放一张从网上找到的框图做简单说明:
二、MediaRecorder子系统分析
MediaRecorder子系统和Camera子系统在结构设计上有异曲同工之妙。只不过因为编码存在软硬件方式,具体实现上是分为官方部分和厂商部分的,以适配不同的编码方案。接下来的分析基于Camera API V1.0进行。
1、官方实现
分析:
2、厂商实现
从StagefrightRecorder往下的实现由于硬件平台的差异性实现会有所不同,但做为适配器它能很好地向上提供消除差异性后的通用使用方式。而做为官方的“Stagefright+OpenMAX”多媒体框架也能降低由于平台差异性而导致的开发难度。在本文中不再对官方多媒体框架做展开描述。
三、Camera和MediaRecorder的配合
单独使用MediaRecorder子系统也能实现预览和录像,但更多见到Camera和MediaRecorder配合使用有两个原因:第一,拍照功能仅由Camera提供;第二,原生实现限制多应用对同一摄像头binder native对象的代理访问。
两大子系统通过访问者模式建立控制和数据传输通道联系,然后分时代理访问(操作流程在Camera.java文件中有很详细的介绍)服务端摄像头binder native对象,以达到预览、录像、拍照目的。在整个操作过程中,Camera(特指具体的类)在中间承担了很重要的责任,看一张省略很多中间环节的简图:
分析:
最后以一张两大子系统配合工作(基于kitkat分析)的关键类的类图结束本章:
分析:
四、学习心得总结
文到此处才真的到了画龙点睛的时候。如前面所言,我学习Android的整体原则是“提纲挈领、重点突破、由线及面“,让这三大原则落地的方法是:
1、先领会(请注意不是“了解”)Android整体设计思想。如果个人领悟力有限完全可以站在别人的肩膀上看风景。如引用一的文章系列,别看是2010年的文章但其深度在我看来没有之一只有唯一,秒杀当前市面上大部分商业书籍的内容,再次证明了“文字贵在精而不在多”这一观点的正确性。
2、然后重点学习系统中具有代表性的子系统或框架,这一类型的实现往往涵盖和体现了系统整体设计的精华。我是从Camera和MediaRecorder两大子系统开始的,为达到掌握细节的目的,我采取的是两个荆棘遍布的非常规步骤:
把Binder机制从框架和驱动相关部分移植到Windows平台运行并调试调用流程。这个过程是极其繁琐和痛苦的,一度有抗拒并放弃的打算,万幸的是坚持了下来。
把两大子系统从上往下的实现剥离出来做为私有子系统用于新增的模拟摄像头和应用开发。由于有第一个步骤的心理准备,所以尽管有难度但心理上已经不抗拒了。
通过这两个实施步骤,我就对Android RPC的核心binder机制原理、以及“应用/服务”的组件思想之妙有了切实的领会,真正做到了“知其然且知其所以然”。
当然,我所采取的方式不一定对所有的人合适,涉及到大量平时躲在角落里的知识点。我之所以采取这种方式,是因为:第一,我之前一直是以Windows平台为主,所以借助自己熟悉的工作环境学习更容易出成果,也能验证其思想的通用性和生命力;第二,我对于平台开发和跨平台移植有多年的经验和心得。但不管采取什么方式,核心观点是“实践是检验真理的唯一标准”。
3、有了前两点的基础,在接下来的工作中根据需要再学习其它的其实就游刃有余了。当然,如果手中有更多的底牌会更有帮助,比如说对于面向对象思想理解(如通过分析类关系在海量代码中快速定位具体实现)、设计模式理解(如快速理解框架整体或局部设计的目的)、操作系统原理及驱动机制理解(如建立更完整的调用层次概念来提升眼界)、跨平台移植了解(如进行平台横向比较分析优劣性来举一反三)等等。
五、引用
1、Android核心分析
http://blog.csdn.net/column/details/androidcore.html
2、Android Camera从Camera HAL1到Camera HAL3的过渡
http://blog.csdn.net/gzzaigcnforever/article/details/48974523