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结合的奇妙世界。编码的乐趣,正在于此!

相关推荐
●VON1 天前
AtomGit Flutter鸿蒙客户端:数据模型
android·服务器·安全·flutter·harmonyos·鸿蒙
火柴就是我1 天前
记录一个文本随手指缩放的功能
android
Zender Han1 天前
Android APK 签名 v1、v2、v3、v4 有什么区别?
android
神仙别闹1 天前
基于 PHP + MySQL学生信息管理系统
android·mysql·php
墨狂之逸才1 天前
Android 保活机制详解 —— 从概念到实践
android
故渊at1 天前
第二板块:Android 四大组件标准化学理 | 第十二篇:四大组件全景总结与系统服务(System Server)架构
android·架构·wpf·四大组件·system service
问心无愧05131 天前
ctf sow web入门112
android·前端·笔记
朱涛的自习室1 天前
Munk AI 正式开源:一个“自我进化”的 AI 测试引擎
android·人工智能·github
啦啦啦_99991 天前
4. Transformer_3_解码器部分
android·深度学习·transformer
数智工坊1 天前
【ROS 2 全栈入门指南三】:Action、参数与Launch文件全链路指南
android·stm32·嵌入式硬件·学习·机器人