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

Kotlin Android如何实现权限获取与蓝牙开启校验以对接ESP32模块

问题解答

1. shouldShowRequestPermissionRationale 与权限请求合约的搭配使用

二者完全可以搭配使用,调用逻辑非常清晰:

  • 你只需要在权限请求回调结果为granted = false时,调用该方法判断用户拒绝权限的场景
  • 方法返回true:说明用户仅单次拒绝权限,未勾选「不再询问」,你可以弹权限用途说明框,用户确认后可再次发起权限申请
  • 方法返回false:说明用户已经勾选「不再询问」,无法再弹出系统权限申请框,直接引导用户去应用设置页手动开启权限即可

2. 精简重复的权限请求合约

你完全不需要定义两个几乎一致的权限请求合约,只需要保留一个ActivityResultContracts.RequestPermission实例,所有分支逻辑都放在同一个回调中通过上述shouldShowRequestPermissionRationale的判断结果处理即可,直接删掉第二个重复的合约即可。

3. 简化重复的状态校验逻辑

你只需要封装一个统一的UI状态同步方法,所有修改conditions属性的位置,修改完成后直接调用该方法即可,不需要到处复制粘贴相同的判断逻辑。


优化后完整代码
const val TAG = "DEBUG"

data class Conditions (var isBtEnabled : Boolean , var permissionsOk :Boolean){
    // 简化返回逻辑
    fun isReady() = isBtEnabled && permissionsOk
    fun log(){
        Log.d("DEBUG","Conditions-> $isBtEnabled , $permissionsOk")
    }
}

class MainActivity : AppCompatActivity() {

    private lateinit var bind: ActivityMainBinding
    private lateinit var broadcastReceiver: BroadcastReceiver
    private lateinit var bluetoothAdapter: BluetoothAdapter
    private var conditions = Conditions(false, false)

    // 统一蓝牙开启结果处理
    private val requestBluetoothEnable =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            Log.d(TAG, "蓝牙开启结果码: ${it.resultCode}")
            if (it.resultCode == RESULT_OK) {
                conditions.isBtEnabled = true
                conditions.log()
            }
            // 直接调用统一状态同步方法
            syncInterfaceState()
        }

    // 仅保留一个权限请求合约
    private val requestLocationPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
            conditions.permissionsOk = checkPermissions()
            if (granted) {
                Log.d(TAG, "位置权限已授予")
                syncInterfaceState()
                return@registerForActivityResult
            }
            // 权限被拒绝,判断是否可以弹说明框
            if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                // 用户未勾选不再询问,弹说明引导再次申请
                AlertDialog.Builder(this@MainActivity)
                    .setTitle("位置权限申请")
                    .setMessage("我需要位置权限才能正常和蓝牙设备通信")
                    .setPositiveButton("确认") { _, _ ->
                        requestLocationPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
                    }
                    .setNegativeButton("取消", null)
                    .show()
                Log.d(TAG, "用户单次拒绝权限,已弹申请引导")
            } else {
                // 用户勾选了不再询问,引导去设置页
                AlertDialog.Builder(this@MainActivity)
                    .setTitle("权限缺失")
                    .setMessage("请前往应用设置页手动开启位置权限,否则应用无法正常工作")
                    .setPositiveButton("知道了", null)
                    .show()
                Log.d(TAG, "用户已勾选不再询问,已弹设置引导")
            }
            syncInterfaceState()
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        bind = ActivityMainBinding.inflate(layoutInflater)
        super.onCreate(savedInstanceState)
        setContentView(bind.root)
        Log.d(TAG, "系统版本 : ${Build.VERSION.SDK_INT} -> ${Build.VERSION.CODENAME}")
        Log.d(TAG, "当前调试版本 : ${getString(R.string.app_name)}")
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

        // 初始化状态
        conditions.isBtEnabled = bluetoothAdapter.isEnabled
        conditions.permissionsOk = checkPermissions()
        Log.d(TAG, "初始状态检查:")
        conditions.log()
        syncInterfaceState()

        // 发起蓝牙开启和权限申请
        if (!bluetoothAdapter.isEnabled) {
            requestBluetoothEnable.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))
        }
        if (!checkPermissions()) {
            requestLocationPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
        }
    }

    // 统一UI状态同步方法,所有状态修改后直接调用即可
    private fun syncInterfaceState() {
        if (conditions.isReady()) {
            buildInterfaceOk()
        } else {
            buildInterfaceError()
        }
    }

    private fun buildInterfaceOk() {
        Log.d(TAG, "构建界面:所有状态正常")
        bind.tvInfo.text = "所有状态正常,可进行后续操作"
    }

    private fun buildInterfaceError() {
        Log.d(TAG, "构建界面:状态异常")
        bind.tvInfo.text = "存在权限或蓝牙未开启问题"
    }

    private fun checkPermissions(): Boolean {
        val permissionsRequired =
            arrayOf(
                Manifest.permission.BLUETOOTH,
                Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        return permissionsRequired.all { permission ->
            val granted = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
            Log.d(TAG, "权限检查:$permission -> ${if (granted) "已授予" else "未授予"}")
            granted
        }
    }
}

内容的提问来源于stack exchange,提问作者Krzysztof Dłuski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 03:06:01