You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Camera2 API无法启用闪光灯问题求助

嘿,我来帮你排查下相机应用里闪光灯开关失效的问题~从你给出的代码片段来看,这里同时用到了CameraManager(Android 6.0+的手电筒API)和Camera2CameraDevice API,这种混用很可能是导致功能失效的核心原因之一。下面我一步步帮你梳理问题和解决办法:

闪光灯开关失效的排查与修复方案

1. 先确认权限是否配置到位

闪光灯功能依赖相机权限,先检查AndroidManifest.xml里有没有添加必要配置:

<!-- 核心权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<!-- 声明设备特性(可选但建议添加) -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />

另外,Android 6.0+必须动态申请相机权限,没有相机权限的话闪光灯根本无法工作。可以在Activity启动时补上权限申请逻辑:

private static final int REQUEST_CAMERA_PERMISSION = 100;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_flash);
    
    // 先检查权限,再初始化闪光灯
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
    } else {
        initFlashLogic();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initFlashLogic();
        } else {
            Toast.makeText(this, "需要相机权限才能使用闪光灯", Toast.LENGTH_SHORT).show();
        }
    }
}

2. 统一API使用方式,避免混用

你代码里同时出现了两种相机API,建议根据需求二选一:

方案一:用CameraManager(仅控制闪光灯,简单快捷)

如果你的需求只是开关闪光灯,完全没必要引入复杂的Camera2会话逻辑,用CameraManager足够。调整后的核心代码:

private CameraManager mCameraManager;
private String mCameraId;
private boolean isTorchOn = false;
private Button mTorchOnOffButton;

private void initFlashLogic() {
    mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        // 找到支持闪光灯的相机ID(一般是后置相机)
        for (String id : mCameraManager.getCameraIdList()) {
            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
            Boolean hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            if (hasFlash != null && hasFlash) {
                mCameraId = id;
                break;
            }
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }

    // 绑定按钮点击事件
    mTorchOnOffButton.setOnClickListener(v -> toggleTorch());
}

private void toggleTorch() {
    try {
        if (mCameraManager != null && mCameraId != null) {
            mCameraManager.setTorchMode(mCameraId, !isTorchOn);
            isTorchOn = !isTorchOn;
            // 更新按钮文本反馈状态
            mTorchOnOffButton.setText(isTorchOn ? "关闭闪光灯" : "打开闪光灯");
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
        Toast.makeText(this, "闪光灯操作失败", Toast.LENGTH_SHORT).show();
    }
}

方案二:用Camera2 API(需要相机预览+闪光灯)

如果你的应用需要同时显示相机预览和控制闪光灯,那就要完整实现Camera2的会话逻辑,绝对不能和CameraManager混用。这里补充你代码里缺失的关键部分:

private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSessions;
private CaptureRequest.Builder captureRequestBuilder;
private boolean isTorchOn = false;
private TextureView textureView;

// CameraDevice状态回调
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice camera) {
        cameraDevice = camera;
        createPreviewSession();
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice camera) {
        camera.close();
        cameraDevice = null;
    }

    @Override
    public void onError(@NonNull CameraDevice camera, int error) {
        camera.close();
        cameraDevice = null;
        Toast.makeText(FlashActivity.this, "相机打开失败", Toast.LENGTH_SHORT).show();
    }
};

// 创建相机预览会话
private void createPreviewSession() {
    try {
        SurfaceTexture texture = textureView.getSurfaceTexture();
        texture.setDefaultBufferSize(640, 480);
        Surface surface = new Surface(texture);

        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        captureRequestBuilder.addTarget(surface);

        cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                cameraCaptureSessions = session;
                updatePreviewFrame();
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                Toast.makeText(FlashActivity.this, "预览配置失败", Toast.LENGTH_SHORT).show();
            }
        }, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

// 更新预览帧
private void updatePreviewFrame() {
    if (cameraDevice == null) return;
    try {
        captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

// 切换闪光灯状态
private void toggleFlash() {
    if (cameraDevice == null || captureRequestBuilder == null) return;
    try {
        isTorchOn = !isTorchOn;
        captureRequestBuilder.set(CaptureRequest.FLASH_MODE, 
            isTorchOn ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
        cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, null);
        mTorchOnOffButton.setText(isTorchOn ? "关闭闪光灯" : "打开闪光灯");
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

额外提醒:使用Camera2时,要给TextureView绑定监听器,在表面可用时再打开相机:

textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        closeCamera();
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
});

// 打开相机
private void openCamera() {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        String cameraId = getBackCameraId(manager);
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            manager.openCamera(cameraId, stateCallback, null);
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

// 获取后置相机ID
private String getBackCameraId(CameraManager manager) throws CameraAccessException {
    for (String id : manager.getCameraIdList()) {
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
        Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
        if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
            return id;
        }
    }
    return manager.getCameraIdList()[0]; // 找不到后置就用第一个相机
}

// 释放相机资源
private void closeCamera() {
    if (cameraCaptureSessions != null) {
        cameraCaptureSessions.close();
        cameraCaptureSessions = null;
    }
    if (cameraDevice != null) {
        cameraDevice.close();
        cameraDevice = null;
    }
}

3. 额外注意事项

  • 提前判断设备是否支持闪光灯(上面的代码已经包含这个逻辑),避免在无闪光灯设备上崩溃
  • 当应用退到后台时,记得关闭闪光灯,避免不必要的耗电
  • 使用Camera2 API时,一定要处理好资源释放,防止内存泄漏

如果还有具体的报错日志或者代码细节,可以补充出来,我再帮你细化排查~

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

火山引擎 最新活动