什么是CameraX?
CameraX是Google推出的Android相机开发库,它是Jetpack库的一部分。相比传统的Camera API和Camera2 API,CameraX提供了更简单、更一致的API接口,同时保持了强大的功能和良好的向后兼容性。
为什么选择CameraX?
传统的相机开发面临诸多挑战:
- 设备碎片化:不同厂商的相机实现差异巨大
- API复杂性:Camera2 API功能强大但使用复杂
- 兼容性问题:需要处理各种Android版本的差异
- 生命周期管理:手动管理相机资源容易出错
CameraX完美解决了这些问题:
- 统一的API接口,屏蔽设备差异
- 自动处理生命周期绑定
- 内置最佳实践和性能优化
- 支持从API 21开始的所有Android版本
CameraX 的核心设计思想
-
生命周期感知: 这是 CameraX 的基石!开发者只需要告诉 CameraX 它需要和哪个生命周期(比如你的 Activity 或 Fragment)绑定。剩下的打开、关闭、释放资源?CameraX 自动搞定!再也不用担心忘记释放相机导致问题了。
-
用例(Use Case)驱动: 开发者通常只需要做几件事:预览、拍照、分析图像。CameraX 把这三件事抽象成了三个核心用例:
PreviewView
:预览你在屏幕上看到的实时画面。ImageCapture
:拍照并保存高质量图片。ImageAnalysis
:分析每一帧图像。
3.CameraSelector (相机选择器) :你想用哪个摄像头?前置还是后置?这个组件专门负责根据你的要求(比如LENS_FACING_BACK)选择一个合适的物理摄像头。
4.ProcessCameraProvider (相机提供者) :它负责协调你的UseCases和CameraSelector,并将它们与应用的生命周期绑定在一起,最终驱动整个拍摄流程。
快速上手
添加依赖(build.gradle)
arduino
dependencies {
implementation "androidx.camera:camera-core:1.3.0"
implementation "androidx.camera:camera-camera2:1.3.0"
implementation "androidx.camera:camera-lifecycle:1.3.0"
implementation "androidx.camera:camera-view:1.3.0"
}
基础实现
1. 布局文件
ini
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/captureButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
2. 核心代码实现
public
private ImageCapture imageCapture;
private ExecutorService cameraExecutor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建一个单线程的执行器,用于相机操作,避免阻塞主线程
cameraExecutor = Executors.newSingleThreadExecutor();
// 请求权限后启动相机
if (allPermissionsGranted()) {
startCamera();
} else {
// 请求权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 10);
}
// 拍照按钮的点击事件
findViewById(R.id.image_capture_button).setOnClickListener(v -> takePhoto());
}
private void startCamera() {
// 1. 获取 ProcessCameraProvider 的实例
// 这是一个异步操作,所以我们添加一个监听器
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
// 成功获取 ProcessCameraProvider
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
// 预览设置
Preview preview = new Preview.Builder().build();
PreviewView previewView = findViewById(R.id.viewFinder);
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 拍照设置
imageCapture = new ImageCapture.Builder().build();
// 3. 选择后置摄像头
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
// 4. 执行bindToLifecycle,这是最关键的一步!
// 在绑定之前,先解绑所有旧的用例
cameraProvider.unbindAll();
// 把相机、生命周期和用例绑定在一起
cameraProvider.bindToLifecycle(
this, // LifecycleOwner,Activity或Fragment自身
cameraSelector, // 相机选择器
preview, // 预览用例
imageCapture); // 拍照用例
} catch (Exception e) {
Log.e("CameraXApp", "用例绑定失败", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void takePhoto() {
// 如果 imageCapture 还没准备好,就直接返回
if (imageCapture == null) {
return;
}
// 1. 创建一个带时间戳的文件来保存图片
String name = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
.format(System.currentTimeMillis());
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
// 2. 创建 OutputFileOptions,指定图片输出的位置和元数据
ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions
.Builder(getContentResolver(),
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues)
.build();
// 3. 执行拍照!这是一个异步操作,我们需要一个回调来处理结果
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this), // 在主线程中执行回调
new ImageCapture.OnImageSavedCallback() { // 结果回调
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
Toast.makeText(MainActivity.this, "照片保存成功: " + output.getSavedUri(), Toast.LENGTH_SHORT).show();
}
@Override
public void onError(@NonNull ImageCaptureException exc) {
Log.e("CameraXApp", "照片保存失败: " + exc.getMessage(), exc);
}
}
);
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraExecutor.shutdown();
}
}
CameraX 带来的好处
- 代码精简: 相比
Camera2
,代码量大幅减少,逻辑更清晰。 - 生命周期无忧: 自动绑定,再也不用写一堆
onResume
/onPause
管理相机开关。 - 兼容性保障: Google 官方背书,持续更新,覆盖绝大多数 Android 5.0+ 设备,省去大量设备适配工作。
- 用例清晰:
Preview
,ImageCapture
,ImageAnalysis
分工明确,按需使用。 - 简化预览:
PreviewView
控件开箱即用,无需手动处理Surface
。
当CameraX遇见MediaPipe
除来了CameraX的基础预览和拍照功能,CameraX最强大的地方,在于它能与各种AI框架无缝集成。MediaPipe就是其中之一。
MediaPipe是什么?
它是Google开源的一个跨平台、可定制的机器学习解决方案框架。简单来说,它提供了大量预训练好的AI模型(如人脸检测、手势识别、姿态估计等),让你能轻松地将强大的AI能力集成到应用中。
还记得上面提到的CameraX第三大用例------ImageAnalysis (图像分析) 吗?这正是连接CameraX和MediaPipe的桥梁。
ImageAnalysis用例不会把图像显示出来,而是会以极高的效率,将相机捕获的每一帧画面数据,通过一个分析器(Analyzer)回调给你。
- 在bindToLifecycle时,除了Preview和ImageCapture,再绑定一个ImageAnalysis用例。
- 创建一个自定义的Analyzer类,在这个类的analyze()方法里,将从CameraX获取的图像帧(ImageProxy对象)喂给MediaPipe的AI模型。
- MediaPipe处理完成后,会返回给我们结构化的数据,比如人脸的关键点坐标、识别出的手势类型等。
- 拿到这些数据后,你就可以在屏幕上绘制各种酷炫的效果了!
希望这部分内容能为你打开一扇新的大门,去探索更多CameraX与AI结合的奇妙世界。编码的乐趣,正在于此!