手电筒App触发setTorchMode-IllegalArgumentException崩溃求助
手电筒App Camera2 API崩溃问题分析与修复
崩溃日志类型
类型1:无效相机ID
Fatal Exception: java.lang.IllegalArgumentException: setTorchMode:2297: Camera ID "0" is a not valid camera ID at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:1332) at android.hardware.camera2.CameraManager$CameraManagerGlobal.setTorchMode(CameraManager.java:1886) at android.hardware.camera2.CameraManager.setTorchMode(CameraManager.java:943) at AccService2.onSensorChanged(AccService2.java:197) at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:1004) at android.os.MessageQueue.nativePollOnce(MessageQueue.java) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loop(Looper.java:206) at android.app.ActivityThread.main(ActivityThread.java:8653) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
类型2:相机无闪光灯单元
Fatal Exception: java.lang.IllegalArgumentException: setTorchMode:2721: Camera "1" does not have a flash unit at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:1723) at android.hardware.camera2.CameraManager$CameraManagerGlobal.setTorchMode(CameraManager.java:2318) at android.hardware.camera2.CameraManager.setTorchMode(CameraManager.java:1211) at AccService2.onSensorChanged(AccService2.java:197) at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:991) at android.os.MessageQueue.nativePollOnce(MessageQueue.java) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loopOnce(Looper.java:186) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8757) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
当前核心代码逻辑
1. 获取带闪光灯的后置相机ID
public class CamId { public String getFirstCameraIdFacing(CameraManager cameraManager, @Nullable Integer facing) { if (facing == null) facing = CameraMetadata.LENS_FACING_BACK; String cameraId = null; try { String[] cameraIdList = cameraManager.getCameraIdList(); for (String id : cameraIdList) { CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); if ( characteristics.get(CameraCharacteristics.LENS_FACING).equals(facing) && characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) { cameraId = id; break; } } if(cameraId == null && cameraIdList.length>0) { cameraId = cameraIdList[0]; } } catch (CameraAccessException e) { Log.e("CameraId ex", "getFirstCameraIdFacing: " + e.getMessage()); } return cameraId; } }
2. 闪光灯可用性监听
public void callBack(){ torchCallback = new CameraManager.TorchCallback() { @Override public void onTorchModeUnavailable(@NonNull String cameraId) { super.onTorchModeUnavailable(cameraId); Flash_Available = false; } @Override public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { super.onTorchModeChanged(cameraId, enabled); flashState = enabled; Flash_Available = true; } }; camManager.registerTorchCallback(torchCallback, null); }
3. 调用setTorchMode前的校验
sCameraId = MAcamID.getFirstCameraIdFacing(camManager,null); if (Flash_Available) { String Cam_id=sCameraId; if (Cam_id != null) { try { camManager.setTorchMode(Cam_id, !flashState); } catch (CameraAccessException e) { Log.e("TAG", "settorchmode Excep", e); } } }
崩溃原因分析
- 相机ID时效性失效:获取相机ID后,设备相机状态可能发生变化(如外接摄像头移除、系统相机资源被抢占、系统更新导致ID失效),但代码未实时校验当前ID有效性。
- Flash_Available状态误判:
- TorchCallback的
onTorchModeChanged无条件设Flash_Available为true,但该回调可能由其他相机的状态变化触发,无法确保当前使用的相机闪光灯可用。 onTorchModeUnavailable仅全局设置状态,未关联当前使用的相机ID,状态更新不精准。
- TorchCallback的
- fallback逻辑风险:找不到带闪光灯的后置相机时,直接返回第一个相机ID,该相机可能本身无闪光灯,后续调用
setTorchMode触发崩溃。 - 时序窗口问题:获取ID、校验状态、调用
setTorchMode之间存在时间差,窗口内相机状态可能已变化,导致校验通过但调用时失效。
修复方案
1. 实时校验相机有效性与闪光灯状态
每次调用setTorchMode前,重新校验当前相机ID的有效性和闪光灯状态,不依赖缓存结果:
sCameraId = MAcamID.getFirstCameraIdFacing(camManager,null); if (sCameraId != null) { try { CameraCharacteristics characteristics = camManager.getCameraCharacteristics(sCameraId); Boolean hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); boolean isCameraIdValid = Arrays.asList(camManager.getCameraIdList()).contains(sCameraId); if (hasFlash != null && hasFlash && isCameraIdValid) { camManager.setTorchMode(sCameraId, !flashState); } } catch (CameraAccessException e) { Log.e("TAG", "settorchmode Excep", e); } }
2. 修正TorchCallback状态判断逻辑
仅针对当前使用的相机ID更新状态,避免误判:
torchCallback = new CameraManager.TorchCallback() { @Override public void onTorchModeUnavailable(@NonNull String cameraId) { super.onTorchModeUnavailable(cameraId); if (cameraId.equals(sCameraId)) { Flash_Available = false; } } @Override public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { super.onTorchModeChanged(cameraId, enabled); if (cameraId.equals(sCameraId)) { flashState = enabled; try { CameraCharacteristics characteristics = camManager.getCameraCharacteristics(cameraId); Boolean hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); Flash_Available = hasFlash != null && hasFlash; } catch (CameraAccessException e) { Flash_Available = false; } } } };
3. 移除不安全的fallback逻辑
找不到带闪光灯的相机时直接返回null,避免调用无闪光灯的设备:
public String getFirstCameraIdFacing(CameraManager cameraManager, @Nullable Integer facing) { if (facing == null) facing = CameraMetadata.LENS_FACING_BACK; String cameraId = null; try { String[] cameraIdList = cameraManager.getCameraIdList(); for (String id : cameraIdList) { CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); if ( characteristics.get(CameraCharacteristics.LENS_FACING).equals(facing) && characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) { cameraId = id; break; } } // 移除原fallback逻辑 } catch (CameraAccessException e) { Log.e("CameraId ex", "getFirstCameraIdFacing: " + e.getMessage()); } return cameraId; }
4. 捕获IllegalArgumentException异常
即使做了前置校验,仍可能因系统底层变化触发崩溃,增加异常捕获避免App闪退:
try { camManager.setTorchMode(sCameraId, !flashState); } catch (CameraAccessException e) { Log.e("TAG", "settorchmode Excep", e); } catch (IllegalArgumentException e) { Log.e("TAG", "Invalid camera ID or no flash", e); // 可在此重新获取相机ID重试,或提示用户闪光灯不可用 }
内容的提问来源于stack exchange,提问作者mohammed said
相关产品推荐
相关产品推荐

