参考链接:Android Camera-CameraX源码分析
如果官网的源码不好下载,推荐一个开源的库:https://github.com/xiandanin/CameraX
一、源码结构
1.文件结构
- camera-core:Camera核心库,设计架构的实现
- camera-camera2:Camera2的配置和操作封装
- camera-view:自定义的 CameraView 组件
- camera-extensions:Camera的扩展,用于访问设备专属供应商效果(例如散景、HDR 及其他功能)的 API
其中camera-core
和camera-camera2
是必须使用的库,使用该库,可以轻松地使用Camera2 API的功能
2.camerax结构
主要看其中几个重要属性:
camera-core\src\main\java\androidx\camera\core
private static final CameraX INSTANCE = new CameraX();
final CameraRepository mCameraRepository = new CameraRepository();
private final AtomicBoolean mInitialized = new AtomicBoolean(false);
private final UseCaseGroupRepository mUseCaseGroupRepository = new UseCaseGroupRepository();
private final ErrorHandler mErrorHandler = new ErrorHandler();
private CameraFactory mCameraFactory;
private CameraDeviceSurfaceManager mSurfaceManager;
private UseCaseConfigFactory mDefaultConfigFactory;
private Context mContext;
主要看其中几个主要的属性:
- CameraRepository:Camera仓库,保存可用 Camera 的列表
- UseCaseGroupRepository:UseCaseGroupLifecycleController实例仓库,每个UseCaseGroupLifecycleController都与一个LifecycleOwner相关联,该LifecycleOwner调节组中所有用例共享的通用生命周期
CameraFactory:Camera抽象工厂,Camera2CameraFactory是具体的实现类
CameraDeviceSurfaceManager:Camera设备与对应数据流管理,具体实现是Camera2DeviceSurfaceManager
UseCaseConfigFactory:UseCase配置工厂
3.CameraX主要使用UseCase
的概念与相机设备进行交互,目前提供的UseCase
:
- 预览(Preview)
- 图片拍摄(ImageCapture)
- 图片分析(ImageAnalysis)
二、CameraX初始化
1.Camera2Initializer
CameraX初始化方法:init
public static void init(Context context, @NonNull AppConfig appConfig) {
INSTANCE.initInternal(context, appConfig);
}
init 是通过ContentProvier配置初始化,具体实现类Camera2Initializer
public final class Camera2Initializer extends ContentProvider {
private static final String TAG = "Camera2Initializer";
@Override
public boolean onCreate() {
Log.d(TAG, "CameraX initializing with Camera2 ...");
CameraX.init(getContext(), Camera2AppConfig.create(getContext()));
return false;
}
...
}
在AndroidMainifest.xml会自动生成provider配置,ContentProvider的OnCreate调用比Applicantion的 onCreate调用更早。
<provider
android:name="androidx.camera.camera2.impl.Camera2Initializer"
android:exported="false"
android:multiprocess="true"
android:authorities="${applicationId}.camerax-init"
android:initOrder="100" />
2.Camera2AppConfig
init 方法传入的 AppConfig 的 create:
public static AppConfig create(Context context) {
// Create the camera factory for creating Camera2 camera objects
CameraFactory cameraFactory = new Camera2CameraFactory(context);
// Create the DeviceSurfaceManager for Camera2
CameraDeviceSurfaceManager surfaceManager = new Camera2DeviceSurfaceManager(context);
// Create default configuration factory
ExtendableUseCaseConfigFactory configFactory = new ExtendableUseCaseConfigFactory();
configFactory.installDefaultProvider(
ImageAnalysisConfig.class, new ImageAnalysisConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
ImageCaptureConfig.class, new ImageCaptureConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
VideoCaptureConfig.class, new VideoCaptureConfigProvider(cameraFactory, context));
configFactory.installDefaultProvider(
PreviewConfig.class, new PreviewConfigProvider(cameraFactory, context));
AppConfig.Builder appConfigBuilder =
new AppConfig.Builder()
.setCameraFactory(cameraFactory)
.setDeviceSurfaceManager(surfaceManager)
.setUseCaseConfigFactory(configFactory);
return appConfigBuilder.build();
}
通过 AppConfig.Builder 进行构建,CameraX中的默认属性都在这里初始化。后面具体讲到某个 UseCase 的时候,详细分析下具体的ConfigProvider
3.CameraX.initInternal
CameraX真正初始化方法:initInternal
private void initInternal(Context context, AppConfig appConfig) {
if (mInitialized.getAndSet(true)) {
return;
}
mContext = context.getApplicationContext();
mCameraFactory = appConfig.getCameraFactory(null);
if (mCameraFactory == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing CameraFactory.");
}
mSurfaceManager = appConfig.getDeviceSurfaceManager(null);
if (mSurfaceManager == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing CameraDeviceSurfaceManager.");
}
mDefaultConfigFactory = appConfig.getUseCaseConfigRepository(null);
if (mDefaultConfigFactory == null) {
throw new IllegalStateException(
"Invalid app configuration provided. Missing UseCaseConfigFactory.");
}
mCameraRepository.init(mCameraFactory);
}
直接从 AppConfig 中获取到具体实例,mCameraFactory对应的实例是Camera2CameraFactory
,mCameraRepository.init(mCameraFactory)进行 Camera 相关的初始化
4.CameraRepository.init
public void init(CameraFactory cameraFactory) {
synchronized (mCamerasLock) {
try {
Set<String> camerasList = cameraFactory.getAvailableCameraIds();
for (String id : camerasList) {
Log.d(TAG, "Added camera: " + id);
mCameras.put(id, cameraFactory.getCamera(id));
}
...
}
}
getAvailableCameraIds
获取可用 Camera Id列表,Camera2CameraFactory的getCamera
真正初始化Camera
public BaseCamera getCamera(@NonNull String cameraId) {
Camera camera = new Camera(mCameraManager, cameraId,
mAvailabilityRegistry.getAvailableCameraCount(), sHandler);
mAvailabilityRegistry.registerCamera(camera);
return camera;
}
通过CameraAvailabilityRegistry的registerCamera方法进行Camera注册
到此为止,CameraX 相关属性就初始化完成了
三、bindToLifecycle
从第一个UseCase预览(preview)
来讲解CameraX 生命周期过程,以及数据传输流程。
前面一篇文章已经讲解过 CameraX 的使用,其中预览(preivew),会先声明PreviewConfig
,通过 config 生成Preivew
,preview.setOnPreviewOutputUpdateListener设置监听Camera数据流。这一系列流程能够实现,主要通过CameraX.bindToLifecycle
实现
具体流程:
public static void bindToLifecycle(LifecycleOwner lifecycleOwner, UseCase... useCases) {
Threads.checkMainThread();
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
INSTANCE.getOrCreateUseCaseGroup(lifecycleOwner);
UseCaseGroup useCaseGroupToBind = useCaseGroupLifecycleController.getUseCaseGroup();
Collection<UseCaseGroupLifecycleController> controllers =
INSTANCE.mUseCaseGroupRepository.getUseCaseGroups();
//检查UseCase 只能在一个lifecycle上
for (UseCase useCase : useCases) {
for (UseCaseGroupLifecycleController controller : controllers) {
UseCaseGroup useCaseGroup = controller.getUseCaseGroup();
if (useCaseGroup.contains(useCase) && useCaseGroup != useCaseGroupToBind) {
throw new IllegalStateException(
String.format(
"Use case %s already bound to a different lifecycle.",
useCase));
}
}
}
//onBind监听回调
for (UseCase useCase : useCases) {
useCase.onBind();
}
calculateSuggestedResolutions(lifecycleOwner, useCases);
for (UseCase useCase : useCases) {
useCaseGroupToBind.addUseCase(useCase);
for (String cameraId : useCase.getAttachedCameraIds()) {
attach(cameraId, useCase);
}
}
useCaseGroupLifecycleController.notifyState();
}
2.UseCaseGroupLifecycleController
创建 UseCaseGroupLifecycleController,UseCaseGroup控制器,通过Lifecycle组件进行 start 和 stop 操作
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
INSTANCE.getOrCreateUseCaseGroup(lifecycleOwner);
...
private UseCaseGroupLifecycleController getOrCreateUseCaseGroup(LifecycleOwner lifecycleOwner) {
return mUseCaseGroupRepository.getOrCreateUseCaseGroup(
lifecycleOwner, new UseCaseGroupRepository.UseCaseGroupSetup() {
@Override
public void setup(UseCaseGroup useCaseGroup) {
useCaseGroup.setListener(mCameraRepository);
}
});
}
通过UseCaseGroupRepository创建UseCaseGroupLifecycleController
UseCaseGroupLifecycleController getOrCreateUseCaseGroup(
LifecycleOwner lifecycleOwner, UseCaseGroupSetup groupSetup) {
UseCaseGroupLifecycleController useCaseGroupLifecycleController;
synchronized (mUseCasesLock) {
//如果有缓存,则直接返回,否则进行创建
useCaseGroupLifecycleController = mLifecycleToUseCaseGroupControllerMap.get(
lifecycleOwner);
if (useCaseGroupLifecycleController == null) {
useCaseGroupLifecycleController = createUseCaseGroup(lifecycleOwner);
groupSetup.setup(useCaseGroupLifecycleController.getUseCaseGroup());
}
}
return useCaseGroupLifecycleController;
}
...
private UseCaseGroupLifecycleController createUseCaseGroup(LifecycleOwner lifecycleOwner) {
...
// Need to add observer before creating UseCaseGroupLifecycleController to make sure
// UseCaseGroups can be stopped before the latest active one is started.
lifecycleOwner.getLifecycle().addObserver(createLifecycleObserver());
UseCaseGroupLifecycleController useCaseGroupLifecycleController =
new UseCaseGroupLifecycleController(lifecycleOwner.getLifecycle());
//创建后,放入 map 缓存
synchronized (mUseCasesLock) {
mLifecycleToUseCaseGroupControllerMap.put(lifecycleOwner,
useCaseGroupLifecycleController);
}
return useCaseGroupLifecycleController;
}
创建UseCaseGroupLifecycleController,并增加Lifecycle生命周期控制:
UseCaseGroupLifecycleController(Lifecycle lifecycle) {
this(lifecycle, new UseCaseGroup());
}
/** Wraps an existing {@link UseCaseGroup} so it is controlled by lifecycle transitions. */
UseCaseGroupLifecycleController(Lifecycle lifecycle, UseCaseGroup useCaseGroup) {
this.mUseCaseGroup = useCaseGroup;
this.mLifecycle = lifecycle;
//绑定Lifecycle
lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.start();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.stop();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy(LifecycleOwner lifecycleOwner) {
synchronized (mUseCaseGroupLock) {
mUseCaseGroup.clear();
}
}
上面的代码,增加了ON_START,ON_STOP,ON_DESTROY
的生命周期监听
2.calculateSuggestedResolutions
根据传入的配置,生成各个UseCase的最佳解决方案。后面的代码会以Preview这个 UseCase 展开,其他 UseCase 代码逻辑类似。
private static void calculateSuggestedResolutions(LifecycleOwner lifecycleOwner,
UseCase... useCases) {
// There will only one lifecycleOwner active. Therefore, only collect use cases belong to
// same lifecycleOwner and calculate the suggested resolutions.
...
// Collect new use cases for different camera devices
for (UseCase useCase : useCases) {
String cameraId = null;
try {
cameraId = getCameraWithCameraDeviceConfig(
(CameraDeviceConfig) useCase.getUseCaseConfig());
} catch (CameraInfoUnavailableException e) {
throw new IllegalArgumentException(
"Unable to get camera id for the camera device config.", e);
}
}
...
// Get suggested resolutions and update the use case session configuration
for (String cameraId : newCameraIdUseCaseMap.keySet()) {
Map<UseCase, Size> suggestResolutionsMap =
getSurfaceManager()
.getSuggestedResolutions(
cameraId,
originalCameraIdUseCaseMap.get(cameraId),
newCameraIdUseCaseMap.get(cameraId));
for (UseCase useCase : newCameraIdUseCaseMap.get(cameraId)) {
Size resolution = suggestResolutionsMap.get(useCase);
Map<String, Size> suggestedCameraSurfaceResolutionMap = new HashMap<>();
suggestedCameraSurfaceResolutionMap.put(cameraId, resolution);
//更新配置
useCase.updateSuggestedResolution(suggestedCameraSurfaceResolutionMap);
}
}
}
每个 UseCase 会去更新对应配置 updateSuggestedResolution->onSuggestedResolutionUpdated
public void updateSuggestedResolution(Map<String, Size> suggestedResolutionMap) {
Map<String, Size> resolutionMap = onSuggestedResolutionUpdated(suggestedResolutionMap);
...
}
onSuggestedResolutionUpdated针对不同的 UseCase 有不同的实现,这里以Preview为例
protected Map<String, Size> onSuggestedResolutionUpdated(
Map<String, Size> suggestedResolutionMap) {
//获取前面配置的 config
PreviewConfig config = (PreviewConfig) getUseCaseConfig();
String cameraId = getCameraIdUnchecked(config);
Size resolution = suggestedResolutionMap.get(cameraId);
...
//设置 config
updateConfigAndOutput(config, resolution);
return suggestedResolutionMap;
}
...
private void updateConfigAndOutput(PreviewConfig config, Size resolution) {
String cameraId = getCameraIdUnchecked(config);
//初始化pipeline
mSessionConfigBuilder = createPipeline(config, resolution);
attachToCamera(cameraId, mSessionConfigBuilder.build());
updateOutput(mSurfaceTextureHolder.getSurfaceTexture(), resolution);
}
3.Preview.createPipeline
创建Preview管道,通过 PreviewConfig 的配置,创建对应的显示Surface和SessionConfig
SessionConfig.Builder createPipeline(PreviewConfig config, Size resolution) {
Threads.checkMainThread();
SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
final CaptureProcessor captureProcessor = config.getCaptureProcessor(null);
//扩展的 extensions实现
if (captureProcessor != null) {
CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
// TODO: To allow user to use an Executor for the processing.
...
} else {
final ImageInfoProcessor processor = config.getImageInfoProcessor(null);
if (processor != null) {
sessionConfigBuilder.addCameraCaptureCallback(new CameraCaptureCallback() {
@Override
public void onCaptureCompleted(
@NonNull CameraCaptureResult cameraCaptureResult) {
super.onCaptureCompleted(cameraCaptureResult);
if (processor.process(
new CameraCaptureResultImageInfo(cameraCaptureResult))) {
notifyUpdated();
}
}
});
}
//默认的 Surface
CheckedSurfaceTexture checkedSurfaceTexture = new CheckedSurfaceTexture(resolution);
mSurfaceTextureHolder = checkedSurfaceTexture;
sessionConfigBuilder.addSurface(checkedSurfaceTexture);
}
...
}
这里就可以看到我们熟悉的味道,在Camera2中 用到的Surface,Session相关配置,后面会用到相关配置。
在CheckedSurfaceTexture中会创建FixedSizeSurfaceTexture用来显示图像。
4.Preview.updateOutput
增加数据的监听
void updateOutput(SurfaceTexture surfaceTexture, Size resolution) {
PreviewConfig useCaseConfig = (PreviewConfig) getUseCaseConfig();
...
PreviewOutput newOutput =
PreviewOutput.create(surfaceTexture, resolution, relativeRotation);
// Only update the output if something has changed
if (!Objects.equals(mLatestPreviewOutput, newOutput)) {
SurfaceTexture oldTexture =
(mLatestPreviewOutput == null)
? null
: mLatestPreviewOutput.getSurfaceTexture();
OnPreviewOutputUpdateListener outputListener = getOnPreviewOutputUpdateListener();
...
if (outputListener != null) {
mSurfaceDispatched = true;
updateListener(outputListener, newOutput);
}
}
}
根据Preview设置的setOnPreviewOutputUpdateListener,获取到对应的Listener,通过updateListener方法回调数据。
private void updateListener(OnPreviewOutputUpdateListener listener, PreviewOutput output) {
...
mOutputUpdateExecutor.execute(() -> listener.onUpdated(output));
...
}
notifyState
调用UseCaseGroupLifecycleController的notifyState,激活 UseCase 状态,在UseCaseGroupLifecycleController中有增加生命周期的监听,在ON_START状态会调用mUseCaseGroup.start方法。
void notifyState() {
synchronized (mUseCaseGroupLock) {
if (mLifecycle.getCurrentState().isAtLeast(State.STARTED)) {
mUseCaseGroup.start();
}
for (UseCase useCase : mUseCaseGroup.getUseCases()) {
useCase.notifyState();
}
}
}
UseCaseGroup.start
void start() {
synchronized (mListenerLock) {
if (mListener != null) {
mListener.onGroupActive(this);
}
mIsActive = true;
}
}
启动 start 状态,调用CameraRepository的onGroupActive方法:
Camera2的预览流程
拍照的流程ImageCapture.takePicture
sendImageCaptureRequest
创建 ImageCaptureRequest,设置cameraId、targetRatio、回调等
private void sendImageCaptureRequest(
@Nullable Executor listenerExecutor, OnImageCapturedListener listener) {
String cameraId = getCameraIdUnchecked(mConfig);
// Get the relative rotation or default to 0 if the camera info is unavailable
int relativeRotation = 0;
try {
CameraInfoInternal cameraInfoInternal = CameraX.getCameraInfo(cameraId);
relativeRotation =
cameraInfoInternal.getSensorRotationDegrees(
mConfig.getTargetRotation(Surface.ROTATION_0));
} catch (CameraInfoUnavailableException e) {
Log.e(TAG, "Unable to retrieve camera sensor orientation.", e);
}
Rational targetRatio = mConfig.getTargetAspectRatioCustom(null);
targetRatio = ImageUtil.rotate(targetRatio, relativeRotation);
mImageCaptureRequests.offer(
new ImageCaptureRequest(relativeRotation, targetRatio, listenerExecutor, listener));
if (mImageCaptureRequests.size() == 1) {
issueImageCaptureRequests();
}
}
takePictureInternal
void issueImageCaptureRequests() {
if (mImageCaptureRequests.isEmpty()) {
return;
}
takePictureInternal();
}
...
//拍照流程
private void takePictureInternal() {
//自定义 Future 调用链
FutureChain.from(preTakePicture(state))
.transformAsync{
...
return ImageCapture.this.issueTakePicture(state);
})
.transformAsync{
...
return ImageCapture.this.postTakePicture(state);
})
.addCallback(
...
onTakePictureFinish(null);
)
}
自定义了整个拍照工作流,通过issueTakePicture
进行拍照,postTakePicture
是拍照成功,释放资源,取消3A。下面重点看下issueTakePicture
流程
issueTakePicture
ListenableFuture<Void> issueTakePicture(TakePictureState state) {
...
getCurrentCameraControl().submitCaptureRequests(captureConfigs);
...
}
通过CameraControl提交Capture 请求,CameraControl具体实现是Camera2CameraControl。
submitCaptureRequests
public void submitCaptureRequests(@NonNull final List<CaptureConfig> captureConfigs) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
submitCaptureRequestsInternal(captureConfigs);
}
});
}
...
void submitCaptureRequestsInternal(final List<CaptureConfig> captureConfigs) {
mControlUpdateListener.onCameraControlCaptureRequests(captureConfigs);
//mControlUpdateListener是Camera 的回调,onCameraControlCaptureRequests 真正实现在 Camera 中
}
//Camera.java
public void onCameraControlUpdateSessionConfig(@NonNull SessionConfig sessionConfig) {
mCameraControlSessionConfig = sessionConfig;
updateCaptureSessionConfig();
}
...
private void updateCaptureSessionConfig() {
...
SessionConfig sessionConfig = validatingBuilder.build();
mCaptureSession.setSessionConfig(sessionConfig);
...
}
}
Camera 获取 Capture的SessionConfig,通过`CaptureSession`进行状态控制
CaptureSession.setSessionConfig
void setSessionConfig(SessionConfig sessionConfig) {
synchronized (mStateLock) {
switch (mState) {
case UNINITIALIZED:
throw new IllegalStateException(
"setSessionConfig() should not be possible in state: " + mState);
case INITIALIZED:
case OPENING:
mSessionConfig = sessionConfig;
break;
case OPENED:
mSessionConfig = sessionConfig;
if (!mConfiguredSurfaceMap.keySet().containsAll(sessionConfig.getSurfaces())) {
Log.e(TAG, "Does not have the proper configured lists");
return;
}
Log.d(TAG, "Attempting to submit CaptureRequest after setting");
issueRepeatingCaptureRequests();
break;
case CLOSED:
case RELEASING:
case RELEASED:
throw new IllegalStateException(
"Session configuration cannot be set on a closed/released session.");
}
}
}
在Camera的OPENED状态,则进行拍照流程
拍照流程 issueRepeatingCaptureRequests
void issueRepeatingCaptureRequests() {
...
CameraCaptureSession.CaptureCallback comboCaptureCallback =
createCamera2CaptureCallback(
captureConfig.getCameraCaptureCallbacks(),
mCaptureCallback);
CameraCaptureSessionCompat.setSingleRepeatingRequest(mCameraCaptureSession,
captureRequest, mExecutor, comboCaptureCallback);
...
}
CameraCaptureSessionCompat根据 Android 版本有CameraCaptureSessionCompatBaseImpl和CameraCaptureSessionCompatApi28Impl两种实现,最终通过CameraCaptureSession实现真正的拍照。
拍照完成后,通过最开始设置的 Listener 进行回调
ImageCapture.createPipeline
在 Preview 那小节,讲解过 bindToLifecycle 流程,这里的ImageCapture也是一个UseCase。在CameraX中的calculateSuggestedResolutions方法,最终会调用到各个UseCase的onSuggestedResolutionUpdated方法。在ImageCapture的onSuggestedResolutionUpdated方法,通过createPipeline创建了拍照数据的回调
SessionConfig.Builder createPipeline(ImageCaptureConfig config, Size resolution) {
...
//和 Camera2的流程一致
mProcessingImageResultThread = new HandlerThread("OnImageAvailableHandlerThread");
mProcessingImageResultThread.start();
mProcessingImageResultHandler = new Handler(mProcessingImageResultThread.getLooper());
...
mImageReader.setOnImageAvailableListener(
new ImageReaderProxy.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReaderProxy imageReader) {
ImageProxy image = null;
try {
image = imageReader.acquireLatestImage();
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to acquire latest image.", e);
} finally {
if (image != null) {
// Call the head request listener to process the captured image.
ImageCaptureRequest imageCaptureRequest;
if ((imageCaptureRequest = mImageCaptureRequests.peek()) != null) {
SingleCloseImageProxy wrappedImage = new SingleCloseImageProxy(
image);
wrappedImage.addOnImageCloseListener(mOnImageCloseListener);
//ImageCaptureRequest设置 Listener
imageCaptureRequest.dispatchImage(wrappedImage);
} else {
// Discard the image if we have no requests.
image.close();
}
}
}
}
},
mProcessingImageResultHandler);
...
}
ImageReader设置了 Camera 数据会调用,并通过ImageCaptureRequest的dispatchImage方法进行分发
ImageCaptureRequest.dispatchImage
void dispatchImage(final ImageProxy image) {
try {
mListenerExecutor.execute(new Runnable() {
@Override
public void run() {
Size sourceSize = new Size(image.getWidth(), image.getHeight());
if (ImageUtil.isAspectRatioValid(sourceSize, mTargetRatio)) {
image.setCropRect(
ImageUtil.computeCropRectFromAspectRatio(sourceSize,
mTargetRatio));
}
//真正的回调
mListener.onCaptureSuccess(image, mRotationDegrees);
}
});
} catch (RejectedExecutionException e) {
Log.e(TAG, "Unable to post to the supplied executor.");
// Unable to execute on the supplied executor, close the image.
image.close();
}
}
mListener是一个封装Listener,在ImageCapture中实现
Listener
Listener的关系图:
+-----------------------+
| |
|ImageCapture. |
|OnImageCapturedListener|
| |
+-----------+-----------+
|
|
+-----------v-----------+ +----------------------+
| | | |
| ImageSaver. | | ImageCapture. |
| OnImageSavedListener +------> OnImageSavedListener |
| | | |
+-----------------------+ +----------------------+
OnImageCapturedListener的实现,其中通过ImageSaver设置的OnImageSavedListener回调到最上层的OnImageSavedListener
OnImageCapturedListener imageCaptureCallbackWrapper =
new OnImageCapturedListener() {
@Override
public void onCaptureSuccess(ImageProxy image, int rotationDegrees) {
CameraXExecutors.ioExecutor()
.execute(
new ImageSaver(
image,
saveLocation,
rotationDegrees,
metadata.isReversedHorizontal,
metadata.isReversedVertical,
metadata.location,
executor,
imageSavedListenerWrapper));
}
...
};
// ImageSaver是一个 Runnable,主要 run 的实现
final class ImageSaver implements Runnable {
...
@Override
public void run() {
...
//图像处理
...
if (saveError != null) {
postError(saveError, errorMessage, exception);
} else {
postSuccess();
}
}
...
private void postSuccess() {
mExecutor.execute(new Runnable() {
@Override
public void run() {
//最外层回调
mListener.onImageSaved(mFile);
}
});
}
}
整个拍照流程和数据回调就讲解完毕了。
通过对 CameraX的 Preview 和 ImageCapture的分析,CameraX对Camera2进行完整的封装,统一参数配置,自动计算Resolution,简化Camera2的开发,并增加了生命周期控制,对外只暴露了简单接口。
使用该库,只需要简单的几行代码就可以实现以前Camera2复杂的操作。