通过前面的一系列文章 ,到现在已经算是学完了Camera 2 API的使用了,也做出一个具体基础功能的相机应用,目前可称得上是一个1.0版本了,后续会在此基础上进行迭代。本篇先进行一个小结。
整体架构
上一篇文章 加入了录像模式,让整体复杂度又提升了一个层次,需要做一次业务逻辑抽离。前面一直使用CameraContext作为整体外部调用的接口,一部分业务逻辑,如启动预览,打开相机,关闭相机,和拍照都放在了CameraContext之中。现在因为多了录像模式,且涉及模式切换,如果仍都把逻辑放入到CameraContext之中,会非常的混乱,于是需要引入新的组件:
classDiagram
CameraScene <|-- BaseScene
BaseScene <|-- PhotoScene
BaseScene <|-- VideoScene
PhotoOutputConfig --|> OutputConfig
VideoOutputConfig --|> OutputConfig
PhotoScene *-- PhotoOutputConfig
VideoScene *-- VideoOutputConfig
BaseScene <-- CameraContext
BaseScene *-- CameraAgent
CameraContext <-- CameraAgent
class CameraScene {
<<interface>>
attachCameraContext(CameraContext cameraContext)
attachSaveAgent(PhotoSaveAgent saveAgent)
activate()
onResume()
onPause()
deactivate()
clickShutter(Consumer~PhotoCaptureStatus~ consumer)
updatePreviewSurface(Surface surface)
setFlashMode(FlashMode flashMode)
changeOutputConfig(OutputConfig config)
switchCamera(Config.CameraFacing facing)
addPreviewSizeListener(Consumer~CameraSize~ consumer)
addStatusListener(Consumer~SceneStatus~ consumer)
getSupportedFlashModes() List~FlashMode~
FlashMode getFlashMode()
getSupportedOutputConfigs() List~OutputConfig~
getOutputConfig() OutputConfig
}
class BaseScene {
Context context
CameraContext.CameraThreadHandler cameraHandler
List~Consumer~CameraSize~~ previewSizeListeners
List~Consumer~SceneStatus~~ moduleStatusListeners
List~FlashMode~ supportedFlashModes
CameraContext cameraContext
CameraAgent cameraAgent
Surface previewSurface
PhotoSaveAgent saveAgent
FlashMode flashMode
notifyStatus(SceneStatus status)
notifyPreviewSize(CameraSize size)
generateOutputTargets() List~Surface~
checkCameraContext()
onCameraAvailable()
}
class VideoScene {
Surface recorderSurface
MediaRecorder mediaRecorder
File emptyFile
List~VideoOutputConfig~ supportedOutputConfigs
VideoOutputConfig currentOutputConfig
}
class PhotoScene {
Optional~OrientationEventListener~ orientationEventListener
int deviceOrientation
List~PhotoOutputConfig~ supportedOutputConfigs
PhotoOutputConfig currentOutputConfig
}
class OutputConfig {
int id
String displayName
Config.PictureRatio ratio
}
class PhotoOutputConfig {
int width
int height
int format
}
class VideoOutputConfig {
int width
int height
int fps
}
class CameraContext {
Context context
CameraManagerWrapper cameraManager
Map~Config.CameraFacing, CameraAgent~ cameraCache
HandlerThread cameraManageThread
CameraThreadHandler cameraThreadHandler
CameraAgent currentCamera
attachThread()
detachThread()
openCamera()
closeCamera()
createScene()
getCurrentCamera() CameraAgent
}
class CameraAgent {
CameraCharacteristics characteristics
Optional~CameraDevice~ cameraDevice
Optional~CameraCaptureSession~ captureSession
PreviewCaptureCallback previewCallback
CaptureRequest.Builder requestBuilder
CameraParameters parameters
calculateSupportedRatios() List~PhotoOutputConfig~
getFlashModes() List~FlashMode~
calculateVideoRecordMode() List~VideoOutputConfig~
connect()
disconnect()
startPreview(List~Surface~)
stopPreview()
capturePhoto()
setFlashMode(FlashMode mode)
}
CameraScene和BaseScene
一个Scene可以定义为一个典型的用户使用场景,比如拍照场景和录像场景,场景是业务逻辑的主要实现者,它的前面是UI层,后面则是平台API封装层。
VideoScene和PhotoScene
目前为止两个使用场景,PhotoScenne负责拍照,从UI处接收命令,完成拍照;VideoScene则负责录像。
与CameraContext的关系
现在CameraContext则是一个纯的线程管理和camera open/close管理,除此之外的其他逻辑全部都挪到了Scene中去。Scene与Context的关系是组合关系,Scene的所有操作都应该只发生在CameraContext的线程中。
线程模型
三个长驻线程,一个是主线程负责UI交互,一个是CameraContext线程,负责所有的业务逻辑,还有一个是存储,一旦结果出来了后余下的所有事情都在存储线程中。
sequenceDiagram
MainThread->>CameraManageThread: startPreview
CameraManageThread-->>MainThread: onPreviewSize
MainThread->>CameraManageThread: capturePhoto
CameraManageThread-->>MainThread: captureStatus
CameraManageThread-) ImageSaveThread: onCaptureComplete
ImageSaveThread-->>MainThread: onThumbnailArrive
组件通信
通过接口进行隔离,均不产生强依赖关系。因为数据 都不复杂,所以用Consumer就可以了,具体的状态根据不同的场景定义一些enum即可。
camera状态反馈
用于隔离Scene和CameraAgent,反馈camera的连接状态,方便做一些与camera强相关的业务处理,比如一些事情(预览和拍照)只能是在camera处于连接状态才能做的事。
1
2
3
4
cameraContext . openCamera ( Config . currentFacing , status -> {
onCameraAvailable ();
notifyStatus ( SceneStatus . ACTIVE );
});
Scene状态反馈
隔离UI和CameraScene,UI的状态,如一些按扭的可点态和显示与隐藏,以及一些显示内容需要知道CameraScene的状态,当CameraScene处于ACTIVE状态时UI才会完全处于可用态。
1
2
3
4
5
6
7
8
9
10
private Consumer < SceneStatus > moduleStatusListener = status -> mainHandler . post (
() -> {
statusView . setText ( status . toString ());
if ( status == SceneStatus . ACTIVE ) {
onSceneActive ();
} else {
onSceneInactive ();
}
}
);
预览尺寸变化 反馈
比较纯,就是用于控制Surface的显示,它需要反馈当前预览的尺寸和比例。
1
2
3
private Consumer < CameraSize > previewSizeListener = size -> mainHandler . post (
() -> viewFinder . setAspectRatio ( size . height , size . width )
);
拍照状态反馈
这个仅在拍照时才需要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final Consumer < PhotoCaptureStatus > captureStatusListener = status -> {
Log . d ( LOG_TAG , "capture status " + status );
statusView . setText ( "Capture Status: " + status );
if ( status == PhotoCaptureStatus . STARTED ) {
overlayView . setBackground ( new ColorDrawable ( Color . argb ( 150 , 255 , 255 , 255 )));
mainHandler . postDelayed (() -> overlayView . setBackground ( null ), Config . CAPTURE_ANIM_DURATION );
} else if ( status == PhotoCaptureStatus . COMPLETED ) {
shutterView . setClickable ( true );
cameraSwitchView . setClickable ( true );
} else if ( status == PhotoCaptureStatus . FAILED ) {
thumbnailSwitcher . setClickable ( true );
shutterView . setClickable ( true );
cameraSwitchView . setClickable ( true );
}
};
后续计划
架构仍需要优化,后面会加入新的功能,未完待续。