说明:camera子系统 系列文章针对Android12.0系统,主要针对 camerax API框架进行解读。
1 CameraX简介
1.1 CameraX 预览流程简要解读
CameraX 是 Android 上的一个 Jetpack 支持库,它提供了一套统一的 API 来处理相机功能,无论 Android 设备上的相机硬件和 Android 版本如何。CameraX 旨在简化相机应用的开发流程,提供更好的兼容性和扩展性。以下是 CameraX 预览的基本流程的简要解读:
-
添加依赖: 在项目的
build.gradle
文件中添加 CameraX 库的依赖。 -
获取 CameraX 核心实例: 使用
CameraX的getCameraProvider()
方法获取 CameraProvider 实例,这是访问 CameraX 功能的基础。 -
配置预览用 Surface: 创建一个
PreviewSurfaceView
或PreviewView
作为相机预览的界面,并配置一个Surface
以供相机输出。 -
构建预览用例: 使用
PreviewConfig.Builder
构建一个预览配置,并将其与PreviewSurface
结合,创建一个PreviewUseCase
。 -
设置预览配置: 通过
CameraProvider
的bindToLifecycle
方法,将预览用例绑定到 Activity 或 Fragment 的生命周期上。注意,在bindToLifecycle
前要先进行unbind操作。 -
启动预览与关闭预览: 调用
PreviewUseCase
的start()
方法开始预览,这会在界面上显示相机捕获的实时画面。 当不再需要预览时,调用PreviewUseCase
的stop()
方法停止预览,并释放相关资源。 -
处理预览数据: 如果需要对预览数据进行处理,比如应用滤镜或进行图像分析,可以通过实现
Preview.PreviewSurfaceProvider
接口来获取Surface
并进行自定义处理。
CameraX 的设计使得相机功能的集成变得更加模块化和易于管理,同时也支持高级功能,如自动对焦、自动曝光和自动白平衡等。
1.2 CameraX与camera2之间的关系
CameraX 和 Camera2 API 都是用于在 Android 设备上访问和控制相机硬件的解决方案,但它们在设计目的、使用方式和目标用户上有所不同。以下是 CameraX 和 Camera2 API 之间的关系说明:
-
Camera2 API:
- 底层访问:Camera2 API 是 Android 平台提供的一个低级相机访问接口,它允许开发者直接与相机硬件交互,进行详细的相机参数配置和控制。
- 复杂性:由于 Camera2 API 提供了对相机硬件的直接控制,使用它需要处理许多复杂的细节,比如处理不同的相机状态、同步问题和性能优化。
- 兼容性:Camera2 API 从 Android 5.0(Lollipop)开始引入,因此它需要在不同版本的 Android 设备上进行兼容性处理。
-
CameraX:
- 简化访问:CameraX 是一个 Jetpack 支持库,旨在简化 Camera2 API 的使用,提供更简单、更一致的相机访问接口。
- 跨版本兼容:CameraX 通过抽象 Camera2 API 的复杂性,为开发者提供一个统一的 API 集,使其能够更容易地在不同版本的 Android 设备上实现相机功能。
- 生命周期感知:CameraX 与 Android 生命周期感知组件(如 Activity 和 Fragment)集成,使得相机功能的管理更加自然和方便。
-
关系:
- 基于 Camera2:CameraX 实际上是在 Camera2 API 的基础上构建的,它封装了 Camera2 API 的复杂性,提供了更高级的抽象。
- 简化开发:对于大多数应用开发者来说,CameraX 提供了一个更简单、更易于使用的接口,使得他们可以不必深入了解 Camera2 API 的细节,就能实现相机功能。
- 扩展性:尽管 CameraX 简化了相机访问,但它仍然保留了扩展性,允许开发者在需要时访问底层的 Camera2 API 功能。
-
使用场景:
- Camera2 API:适合那些需要对相机硬件进行精细控制的应用,比如专业的摄影应用或需要特定相机功能的应用。
- CameraX:适合大多数需要相机功能的应用,特别是那些希望快速集成相机功能并减少开发复杂性的应用。
总结来说,CameraX 可以看作是 Camera2 API 的一个高级封装,它为开发者提供了一个更简单、更一致的接口来访问和控制 Android 设备上的相机硬件。通过使用 CameraX,开发者可以更容易地实现相机功能,同时减少对底层 Camera2 API 的依赖。
2 预览流程代码完整解读(android Q)
2.1 添加deps依赖
在项目的 build.gradle
文件中添加 CameraX 库的依赖。build.gradle 中添加deps,具体如下:
dependencies {...implementation libs.camera.viewimplementation "androidx.camera:camera-core:1.3.4"
// CameraX Camera2 extensions[可选]拓展库可实现人像、HDR、夜间和美颜、滤镜但依赖于OEMimplementation "androidx.camera:camera-camera2:1.3.4"
// CameraX Lifecycle library[可选]避免手动在生命周期释放和销毁数据implementation "androidx.camera:camera-lifecycle:1.3.4"
// CameraX View class[可选]最佳实践,最好用里面的PreviewView,它会自行判断用SurfaceView还是TextureView来实现implementation libs.androidx.camera.view.v100alpha23...
}
2.2 layout布局文件的构建
布局文件 h264_encode_camerax.xml(可自定义) 内容如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/main"tools:context=".MainActivity"><androidx.camera.view.PreviewViewandroid:id="@+id/viewFinder"android:layout_width="372dp"android:layout_height="240dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"/><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:text="@string/startCapture"android:id="@+id/button"app:layout_constraintTop_toBottomOf="@id/viewFinder"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintHorizontal_bias="0.5"/></androidx.constraintlayout.widget.ConstraintLayout>
2.3 关于权限部分的处理
关于权限,需要在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.CAMERA"/>
关于权限的请求等,这里给出一个工具类参考代码:
public class Permission {public static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 1;//需要申请权限的数组private static final String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA};//保存真正需要去申请的权限private static final List<String> permissionList = new ArrayList<>();public static int RequestCode = 100;public static void requestManageExternalStoragePermission(Context context, Activity activity) {if (!Environment.isExternalStorageManager()) {showManageExternalStorageDialog(activity);}}private static void showManageExternalStorageDialog(Activity activity) {AlertDialog dialog = new AlertDialog.Builder(activity).setTitle("权限请求").setMessage("请开启文件访问权限,否则应用将无法正常使用。").setNegativeButton("取消", null).setPositiveButton("确定", (dialogInterface, i) -> {Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);}).create();dialog.show();}public static void checkPermissions(Activity activity) {for (String permission : permissions) {if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {permissionList.add(permission);}}if (!permissionList.isEmpty()) {requestPermission(activity);}}public static void requestPermission(Activity activity) {ActivityCompat.requestPermissions(activity,permissionList.toArray(new String[0]),RequestCode);}
}
该代码主要是给后面即将提到的主代码使用。
2.4 CameraX主流程代码参考实现
这里以 H264encoderCameraXActivity 为例,给出一个预览流程代码的参考实现。代码如下:
public class H264encoderCameraXActivity extends AppCompatActivity {private Button mButton;Context mContext;private PreviewView previewView;private ImageAnalysis imageAnalysis;private ExecutorService executor;private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;private boolean isCapturePreview = false;ProcessCameraProvider mCameraProvider;Preview mPreview;H264Encode h264Encode = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);mContext = this;setContentView(R.layout.h264_encode_camerax);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});//权限处理Permission.checkPermissions(this);Permission.requestManageExternalStoragePermission(getApplicationContext(), this);executor = Executors.newSingleThreadExecutor();previewView = findViewById(R.id.viewFinder);mButton = findViewById(R.id.button);mButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {isCapturePreview = !isCapturePreview;if(isCapturePreview){mButton.setText(R.string.startCapture);startCamera();}else{mButton.setText(R.string.stopCapture);stopCamera();}}});// 初始化 ImageAnalysisimageAnalysis = new ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {@Overridepublic void analyze(@NonNull ImageProxy imageProxy) {Log.d("XXXX","-----------------get Data");// 处理图像数据}});}private void startCamera() {// 请求 CameraProvidercameraProviderFuture = ProcessCameraProvider.getInstance(this);//检查 CameraProvider 可用性,验证它能否在视图创建后成功初始化cameraProviderFuture.addListener(() -> {try {mCameraProvider = cameraProviderFuture.get();bindPreview(mCameraProvider);} catch (ExecutionException | InterruptedException e) {e.printStackTrace();}}, ContextCompat.getMainExecutor(this));}//选择相机并绑定生命周期和用例private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {mPreview = new Preview.Builder().build();CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();cameraProvider.unbindAll();cameraProvider.bindToLifecycle(this, cameraSelector, mPreview, imageAnalysis);mPreview.setSurfaceProvider(previewView.getSurfaceProvider());}private void stopCamera() {if ((mCameraProvider != null) && mCameraProvider.isBound(mPreview)) {mCameraProvider.unbindAll();imageAnalysis.clearAnalyzer();executor.shutdown();}}@Overrideprotected void onDestroy() {super.onDestroy();if (imageAnalysis != null) {imageAnalysis.clearAnalyzer(); // 清除分析器}if (executor != null) {executor.shutdown(); // 关闭线程池}}
}
这里和CameraX相关的方法主要是startCamera、bindPreview和stopCamera。
2.5 CameraX预览demo实现效果
实际运行效果展示如下: