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
问题排查与修复方案
核心问题点
- CameraX调用顺序错误:先执行
takePicture()再绑定ImageCapture到相机生命周期,此时用例未和相机关联,直接触发拍照必然失败,日志中的Image capture error就是这个原因导致。 - 权限处理不完整:
checkCameraPermissions方法缺少Context、Manifest等类的导入,且未处理权限申请后的回调,用户授予权限后不会重新初始化相机。 - 缺少相机预览组件:仅绑定
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()); } }); } }
配套布局与依赖配置
- 布局文件
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>
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
相关产品推荐
相关产品推荐

