说明: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.view
implementation "androidx.camera:camera-core:1.3.4"
// CameraX Camera2 extensions[可选]拓展库可实现人像、HDR、夜间和美颜、滤镜但依赖于OEM
implementation "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.PreviewView
android: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"
/>
<Button
android: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;
@Override
protected 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() {
@Override
public void onClick(View view) {
isCapturePreview = !isCapturePreview;
if(isCapturePreview){
mButton.setText(R.string.startCapture);
startCamera();
}else{
mButton.setText(R.string.stopCapture);
stopCamera();
}
}
});
// 初始化 ImageAnalysis
imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
@Override
public void analyze(@NonNull ImageProxy imageProxy) {
Log.d("XXXX","-----------------get Data");
// 处理图像数据
}
});
}
private void startCamera() {
// 请求 CameraProvider
cameraProviderFuture = 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();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (imageAnalysis != null) {
imageAnalysis.clearAnalyzer(); // 清除分析器
}
if (executor != null) {
executor.shutdown(); // 关闭线程池
}
}
}
这里和CameraX相关的方法主要是startCamera、bindPreview和stopCamera。
2.5 CameraX预览demo实现效果
实际运行效果展示如下: