You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CameraX无法启动相机:Android图像捕获功能实现问题求助

Android CameraX拍照功能故障排查

我是Android开发新手,想实现APP内拍照并上传服务器的功能,但下面的代码无法正常启动相机,麻烦帮忙找出问题。

问题代码

package com.example.scanner;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;

import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;

import com.google.common.util.concurrent.ListenableFuture;

import java.io.File;
import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, Runnable {
    ListenableFuture<ProcessCameraProvider> instance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View button = findViewById(R.id.button_camera);

        button.setOnClickListener(this);

    }

    public static void checkCameraPermissions(Context context){
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED)
        {
            // Permission is not granted
            ActivityCompat.requestPermissions((Activity) context,
                    new String[] { Manifest.permission.CAMERA },
                    100);
        }
    }

    @Override
    public void onClick(View view) {
        checkCameraPermissions(this);
        instance = ProcessCameraProvider.getInstance(this);
        instance.addListener(this, ContextCompat.getMainExecutor(this));
    }

    @Override
    public void run() {
        CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
        ImageCapture imageCapture = new ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_OFF)
                .setTargetAspectRatio(AspectRatio.RATIO_4_3).build();
        imageCapture.takePicture(ContextCompat.getMainExecutor(this), new ImageCapture.OnImageCapturedCallback() {
            @Override
            public void onCaptureSuccess(@NonNull ImageProxy image) {
                super.onCaptureSuccess(image);
                System.out.println("Image captured");

            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                super.onError(exception);
                System.out.println("Image capture error");
            }
        });
        try {
            instance.get().unbindAll();
            Camera camera = instance.get().bindToLifecycle(this, cameraSelector, imageCapture);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

日志信息

I/CameraManagerGlobal: Connecting to camera service
D/CameraRepository: Added camera: 0
I/Camera2CameraInfo: Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
D/CameraRepository: Added camera: 1
I/Camera2CameraInfo: Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_FULL
D/CameraValidator: Verifying camera lens facing on generic_x86, lensFacingInteger: null
D/DeferrableSurface: Surface created[total_surfaces=1, used_surfaces=0](androidx.camera.core.impl.ImmediateSurface@b594e82}
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Use case androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950 ACTIVE
I/System.out: Image capture error
D/UseCaseAttachState: Active and attached use case: [] for camera: 1
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Use case androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950 ACTIVE
D/UseCaseAttachState: Active and attached use case: [] for camera: 1
D/UseCaseAttachState: Active and attached use case: [] for camera: 1
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Use cases [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950] now ATTACHED
D/UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950] for camera: 1
D/MeteringRepeating: MeteringSession SurfaceTexture size: 640x480
D/DeferrableSurface: Surface created[total_surfaces=2, used_surfaces=0](androidx.camera.core.impl.ImmediateSurface@771480b}
D/UseCaseAttachState: Active and attached use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950, MeteringRepeating140716966] for camera: 1
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Resetting Capture Session
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Releasing session in state INITIALIZED
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Attempting to force open the camera.
D/CameraStateRegistry: tryOpenCamera(Camera@f05aa9c[id=1]) [Available Cameras: 1, Already Open: false (Previous state: null)] --> SUCCESS
D/CameraStateRegistry: Recalculating open cameras:
    Camera                                       State                 
    -------------------------------------------------------------------
    Camera@ff47ced[id=0]                         UNKNOWN               
    Camera@f05aa9c[id=1]                         OPENING               
    -------------------------------------------------------------------
    Open count: 1 (Max allowed: 1)
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Opening camera.
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Transitioning camera internal state: INITIALIZED --> OPENING
D/CameraStateMachine: New public camera state CameraState{type=OPENING, error=null} from OPENING and null
    Publishing new public camera state CameraState{type=OPENING, error=null}
D/UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950, MeteringRepeating140716966] for camera: 1
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Use case androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950 ACTIVE
D/UseCaseAttachState: Active and attached use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950, MeteringRepeating140716966] for camera: 1
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} CameraDevice.onOpened()
D/Camera2CameraImpl: {Camera@f05aa9c[id=1]} Transitioning camera internal state: OPENING --> OPENED
D/CameraStateRegistry: Recalculating open cameras:
    Camera                                       State                 
    -------------------------------------------------------------------
    Camera@ff47ced[id=0]                         UNKNOWN               
    Camera@f05aa9c[id=1]                         OPEN                  
    -------------------------------------------------------------------
    Open count: 1 (Max allowed: 1)
D/CameraStateMachine: New public camera state CameraState{type=OPEN, error=null} from OPEN and null
D/CameraStateMachine: Publishing new public camera state CameraState{type=OPEN, error=null}
D/UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950, MeteringRepeating140716966] for camera: 1
D/UseCaseAttachState: Active and attached use case: [androidx.camera.core.ImageCapture-13259d05-6a33-46c4-a2da-b6445df075d450632950, MeteringRepeating140716966] for camera: 1
D/SyncCaptureSessionBase: [androidx.camera.camera2.internal.SynchronizedCaptureSessionBaseImpl@96849fb] getSurface...done
D/CaptureSession: Opening capture session.
D/DeferrableSurface: New surface in use[total_surfaces=2, used_surfaces=1](androidx.camera.core.impl.ImmediateSurface@b594e82}
    use count+1, useCount=1 androidx.camera.core.impl.ImmediateSurface@b594e82
    New surface in use[total_surfaces=2, used_surfaces=2](androidx.camera.core.impl.ImmediateSurface@771480b}
D/DeferrableSurface: use count+1, useCount=1 androidx.camera.core.impl.ImmediateSurface@771480b
D/CaptureSession: Attempting to send capture request onConfigured
D/CaptureSession: Issuing request for session.
W/Gralloc4: allocator 3.x is not supported
D/CaptureSession: CameraCaptureSession.onConfigured() mState=OPENED
D/CaptureSession: CameraCaptureSession.onReady() OPENED

问题排查与修复方案

核心问题点

  1. CameraX调用顺序错误:先执行takePicture()再绑定ImageCapture到相机生命周期,此时用例未和相机关联,直接触发拍照必然失败,日志中的Image capture error就是这个原因导致。
  2. 权限处理不完整:checkCameraPermissions方法缺少Context、Manifest等类的导入,且未处理权限申请后的回调,用户授予权限后不会重新初始化相机。
  3. 缺少相机预览组件:仅绑定ImageCapture无预览,用户看不到相机画面,部分设备还会因为没有预览表面导致相机无法正常工作。

修复后的完整代码

package com.example.scanner;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final int CAMERA_PERMISSION_REQUEST_CODE = 100;
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
    private ImageCapture imageCapture;
    private PreviewView previewView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        previewView = findViewById(R.id.preview_view);
        Button cameraButton = findViewById(R.id.button_camera);
        cameraButton.setOnClickListener(this);

        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    }

    @Override
    public void onClick(View view) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CAMERA},
                    CAMERA_PERMISSION_REQUEST_CODE);
        } else {
            startCamera();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startCamera();
            }
        }
    }

    private void startCamera() {
        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

                // 初始化预览用例
                Preview preview = new Preview.Builder()
                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                        .build();
                preview.setSurfaceProvider(previewView.getSurfaceProvider());

                // 初始化拍照用例
                imageCapture = new ImageCapture.Builder()
                        .setFlashMode(ImageCapture.FLASH_MODE_OFF)
                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                        .build();

                // 选择后置相机(默认前置体验不佳,可根据需求修改)
                CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;

                // 解绑所有用例,重新绑定
                cameraProvider.unbindAll();
                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);

            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    // 单独抽离拍照方法,可在按钮点击时调用
    private void takePhoto() {
        if (imageCapture == null) return;

        Executor executor = ContextCompat.getMainExecutor(this);
        imageCapture.takePicture(executor, new ImageCapture.OnImageCapturedCallback() {
            @Override
            public void onCaptureSuccess(@NonNull ImageProxy image) {
                super.onCaptureSuccess(image);
                System.out.println("Image captured");
                // 必须关闭ImageProxy释放资源
                image.close();
                // 此处可添加图片上传服务器的逻辑
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                super.onError(exception);
                System.out.println("Image capture error: " + exception.getMessage());
            }
        });
    }
}

配套布局与依赖配置

  1. 布局文件activity_main.xml:添加预览组件显示相机画面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <Button
        android:id="@+id/button_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="拍照"/>

</LinearLayout>
  1. build.gradle依赖配置:确保添加CameraX相关库
dependencies {
    def camerax_version = "1.3.0"
    implementation "androidx.camera:camera-core:$camerax_version"
    implementation "androidx.camera:camera-camera2:$camerax_version"
    implementation "androidx.camera:camera-view:$camerax_version"
}

内容的提问来源于stack exchange,提问作者Zeus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:40:25