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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:19:59