如何在Jetpack Compose应用中实现Android13手电筒亮度调节(绕过API35限制)
在Jetpack Compose中兼容Android 13+的手电筒亮度控制(绕过API35限制)
问题本质
官方在API35才正式公开CameraCharacteristics.FLASH_TORCH_STRENGTH_MAX_LEVEL这类手电筒亮度相关常量,但Android 13(API33)及以上系统已经通过Camera2 API支持亮度调节——只是这些字段被标记为@hide(隐藏)。Flashlight-Tiramisu这类应用正是靠反射调用这些隐藏字段实现的兼容,根本没依赖API35。
具体解决方案
1. 反射获取隐藏的Camera2常量
先通过反射拿到API33+系统中已存在的亮度相关隐藏字段:
private fun getTorchStrengthConstants(): Pair<Int, Int>? { return try { // 反射获取隐藏的常量字段 val maxLevelField = CameraCharacteristics::class.java.getField("FLASH_TORCH_STRENGTH_MAX_LEVEL") val defaultLevelField = CameraCharacteristics::class.java.getField("FLASH_TORCH_STRENGTH_DEFAULT_LEVEL") // 取出字段对应的Key值 val maxLevelKey = maxLevelField.get(null) as Int val defaultLevelKey = defaultLevelField.get(null) as Int Pair(maxLevelKey, defaultLevelKey) } catch (e: Exception) { // 字段不存在或反射失败时返回null null } }
2. 动态实现亮度获取与调节
结合运行时API版本判断,在Android 13+系统中使用反射字段操作亮度,无需标注@RequiresApi(35):
// 获取手电筒最大亮度等级 fun getTorchMaxLevel(context: Context): Int { // 仅在Android 13及以上尝试获取 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { return -1 } return try { val cameraManager = ContextCompat.getSystemService(context, CameraManager::class.java) as CameraManager // 找到带闪光灯的后置摄像头 val cameraId = cameraManager.cameraIdList.firstOrNull { val characteristics = cameraManager.getCameraCharacteristics(it) val hasFlash = characteristics[CameraCharacteristics.FLASH_INFO_AVAILABLE] ?: false val isBackFacing = characteristics[CameraCharacteristics.LENS_FACING] == CameraCharacteristics.LENS_FACING_BACK hasFlash && isBackFacing } ?: return -1 val characteristics = cameraManager.getCameraCharacteristics(cameraId) val (maxLevelKey, _) = getTorchStrengthConstants() ?: return -1 // 返回最大亮度等级 characteristics.get(maxLevelKey) as? Int ?: -1 } catch (e: Exception) { -1 } } // 设置手电筒亮度的示例函数 fun setTorchStrength(context: Context, strength: Int) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return try { val cameraManager = ContextCompat.getSystemService(context, CameraManager::class.java) as CameraManager val cameraId = cameraManager.cameraIdList.firstOrNull { val characteristics = cameraManager.getCameraCharacteristics(it) val hasFlash = characteristics[CameraCharacteristics.FLASH_INFO_AVAILABLE] ?: false val isBackFacing = characteristics[CameraCharacteristics.LENS_FACING] == CameraCharacteristics.LENS_FACING_BACK hasFlash && isBackFacing } ?: return // 校验亮度值是否在有效范围 val maxLevel = getTorchMaxLevel(context) if (strength < 0 || strength > maxLevel) return val (_, defaultLevelKey) = getTorchStrengthConstants() ?: return // 组装亮度参数 val params = mutableMapOf<Int, Any>().apply { put(defaultLevelKey, strength) } // 打开手电筒并设置亮度 cameraManager.setTorchMode(cameraId, true) cameraManager.setTorchModeWithStrengthLevel(cameraId, true, params) } catch (e: Exception) { // 处理异常:权限不足、设备不支持等情况 } }
3. Compose集成注意事项
- 权限处理:必须在Manifest中声明
android.permission.CAMERA,并在运行时申请权限,否则无法访问CameraManager。Compose中可以用rememberPermissionState快速实现:
@Composable fun FlashlightControl() { val context = LocalContext.current val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA) if (!cameraPermissionState.hasPermission) { Button(onClick = { cameraPermissionState.launchPermissionRequest() }) { Text("请求相机权限") } } else { val maxLevel = remember { getTorchMaxLevel(context) } var currentStrength by remember { mutableStateOf(0) } Slider( value = currentStrength.toFloat(), onValueChange = { newVal -> currentStrength = newVal.toInt() setTorchStrength(context, currentStrength) }, valueRange = 0f..maxLevel.toFloat() ) } }
关键提醒
- 反射依赖系统内部字段,部分定制ROM可能修改或移除这些字段,必须做好异常捕获。
- 测试时覆盖不同品牌的Android 13+设备,确保兼容性。
- 未来API35成为主流后,可逐步替换为官方公开API,移除反射逻辑。
内容的提问来源于stack exchange,提问作者Mehedi Hasan
相关产品推荐
相关产品推荐

