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
相关产品推荐
相关产品推荐

