CameraX:Android相机开发的现代化解决方案

什么是CameraX?

CameraX是Google推出的Android相机开发库,它是Jetpack库的一部分。相比传统的Camera API和Camera2 API,CameraX提供了更简单、更一致的API接口,同时保持了强大的功能和良好的向后兼容性。

为什么选择CameraX?

传统的相机开发面临诸多挑战:

  • 设备碎片化:不同厂商的相机实现差异巨大
  • API复杂性:Camera2 API功能强大但使用复杂
  • 兼容性问题:需要处理各种Android版本的差异
  • 生命周期管理:手动管理相机资源容易出错

CameraX完美解决了这些问题:

  • 统一的API接口,屏蔽设备差异
  • 自动处理生命周期绑定
  • 内置最佳实践和性能优化
  • 支持从API 21开始的所有Android版本

CameraX 的核心设计思想

  1. 生命周期感知: 这是 CameraX 的基石!开发者只需要告诉 CameraX 它需要和哪个生命周期(比如你的 Activity 或 Fragment)绑定。剩下的打开、关闭、释放资源?CameraX 自动搞定!再也不用担心忘记释放相机导致问题了。

  2. 用例(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 带来的好处

  1. 代码精简: 相比 Camera2,代码量大幅减少,逻辑更清晰。
  2. 生命周期无忧: 自动绑定,再也不用写一堆 onResume/onPause 管理相机开关。
  3. 兼容性保障: Google 官方背书,持续更新,覆盖绝大多数 Android 5.0+ 设备,省去大量设备适配工作。
  4. 用例清晰: Preview, ImageCapture, ImageAnalysis 分工明确,按需使用。
  5. 简化预览: PreviewView 控件开箱即用,无需手动处理 Surface

当CameraX遇见MediaPipe

除来了CameraX的基础预览和拍照功能,CameraX最强大的地方,在于它能与各种AI框架无缝集成。MediaPipe就是其中之一。

MediaPipe是什么?

它是Google开源的一个跨平台、可定制的机器学习解决方案框架。简单来说,它提供了大量预训练好的AI模型(如人脸检测、手势识别、姿态估计等),让你能轻松地将强大的AI能力集成到应用中。

还记得上面提到的CameraX第三大用例------ImageAnalysis (图像分析) 吗?这正是连接CameraX和MediaPipe的桥梁。

ImageAnalysis用例不会把图像显示出来,而是会以极高的效率,将相机捕获的每一帧画面数据,通过一个分析器(Analyzer)回调给你。

  1. 在bindToLifecycle时,除了Preview和ImageCapture,再绑定一个ImageAnalysis用例。
  2. 创建一个自定义的Analyzer类,在这个类的analyze()方法里,将从CameraX获取的图像帧(ImageProxy对象)喂给MediaPipe的AI模型。
  3. MediaPipe处理完成后,会返回给我们结构化的数据,比如人脸的关键点坐标、识别出的手势类型等。
  4. 拿到这些数据后,你就可以在屏幕上绘制各种酷炫的效果了!

希望这部分内容能为你打开一扇新的大门,去探索更多CameraX与AI结合的奇妙世界。编码的乐趣,正在于此!

相关推荐
qq_3168377524 分钟前
win11 使用adb 获取安卓系统日志
android·adb
火凤凰--凤凰码路26 分钟前
MySQL 中的“双路排序”与“单路排序”:原理、判别与实战调优
android·数据库·mysql
wayne21435 分钟前
Android ContentProvider详解:底层原理与最佳实践
android
一个天蝎座 白勺 程序猿1 小时前
Python(32)Python内置函数全解析:30个核心函数的语法、案例与最佳实践
android·开发语言·python
_一条咸鱼_3 小时前
Android Runtime性能计数器实现深度剖析(95)
android·android jetpack
yzpyzp4 小时前
Android 的16 KB内存页设备需要硬件支持吗,还是只需要手机升级到Android15系统就可以
android·智能手机
yzpyzp4 小时前
目前市面上arm64-v8a、armeabi-v7a设备的市占率有多少?为什么x86架构的手机越来越少?
android·gradle·cpu·ndk
yzpyzp5 小时前
Android studio自带的Android模拟器都是x86架构的吗,需要把arm架构的app翻译成x86指令?
android·android studio·cpu