184 lines
5.3 KiB
Markdown
184 lines
5.3 KiB
Markdown
|
|
# 蓝牙权限申请指南
|
|||
|
|
|
|||
|
|
## 🔐 权限说明
|
|||
|
|
|
|||
|
|
在Android 12及以上版本,使用蓝牙功能需要动态申请以下权限:
|
|||
|
|
|
|||
|
|
### 必需权限
|
|||
|
|
1. **BLUETOOTH_SCAN** - 扫描蓝牙设备
|
|||
|
|
2. **BLUETOOTH_CONNECT** - 连接蓝牙设备
|
|||
|
|
3. **ACCESS_FINE_LOCATION** - 精确位置(蓝牙扫描需要)
|
|||
|
|
4. **ACCESS_COARSE_LOCATION** - 粗略位置(蓝牙扫描需要)
|
|||
|
|
|
|||
|
|
## 📱 权限申请流程
|
|||
|
|
|
|||
|
|
### 首次启动应用
|
|||
|
|
1. 应用启动时会显示当前权限状态
|
|||
|
|
2. 点击"连接蓝牙"按钮
|
|||
|
|
3. 系统弹出权限申请对话框
|
|||
|
|
4. 选择"允许"授予所有权限
|
|||
|
|
|
|||
|
|
### 权限状态显示
|
|||
|
|
```
|
|||
|
|
权限状态:
|
|||
|
|
BLUETOOTH_SCAN: ✓
|
|||
|
|
BLUETOOTH_CONNECT: ✓
|
|||
|
|
ACCESS_FINE_LOCATION: ✓
|
|||
|
|
ACCESS_COARSE_LOCATION: ✓
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ⚠️ 常见问题
|
|||
|
|
|
|||
|
|
### 1. 权限被拒绝
|
|||
|
|
**症状**:按钮显示"权限被拒绝",无法使用蓝牙功能
|
|||
|
|
**解决**:
|
|||
|
|
- 进入系统设置 → 应用管理 → 本应用 → 权限
|
|||
|
|
- 手动开启蓝牙和位置权限
|
|||
|
|
- 重新启动应用
|
|||
|
|
|
|||
|
|
### 2. 部分权限缺失
|
|||
|
|
**症状**:提示"缺少权限: BLUETOOTH_SCAN, ACCESS_FINE_LOCATION"
|
|||
|
|
**解决**:
|
|||
|
|
- 确保所有4个权限都已授予
|
|||
|
|
- 位置权限对于蓝牙扫描是必需的
|
|||
|
|
|
|||
|
|
### 3. 权限申请对话框不出现
|
|||
|
|
**症状**:点击按钮无反应
|
|||
|
|
**解决**:
|
|||
|
|
- 检查应用是否被系统限制
|
|||
|
|
- 重启应用或设备
|
|||
|
|
- 检查系统版本是否支持
|
|||
|
|
|
|||
|
|
## 🔧 技术实现
|
|||
|
|
|
|||
|
|
### 权限检查代码
|
|||
|
|
```kotlin
|
|||
|
|
private fun checkAndRequestPermissions(): Boolean {
|
|||
|
|
val missingPermissions = mutableListOf<String>()
|
|||
|
|
|
|||
|
|
for (permission in REQUIRED_PERMISSIONS) {
|
|||
|
|
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
|||
|
|
missingPermissions.add(permission)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (missingPermissions.isNotEmpty()) {
|
|||
|
|
updateStatus("需要蓝牙权限: ${missingPermissions.joinToString(", ")}")
|
|||
|
|
ActivityCompat.requestPermissions(
|
|||
|
|
this,
|
|||
|
|
missingPermissions.toTypedArray(),
|
|||
|
|
PERMISSION_REQUEST_CODE
|
|||
|
|
)
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 权限结果处理
|
|||
|
|
```kotlin
|
|||
|
|
override fun onRequestPermissionsResult(
|
|||
|
|
requestCode: Int,
|
|||
|
|
permissions: Array<out String>,
|
|||
|
|
grantResults: IntArray
|
|||
|
|
) {
|
|||
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|||
|
|
|
|||
|
|
when (requestCode) {
|
|||
|
|
PERMISSION_REQUEST_CODE -> {
|
|||
|
|
val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
|
|||
|
|
|
|||
|
|
if (allGranted) {
|
|||
|
|
updateStatus("蓝牙权限已授予,开始扫描...")
|
|||
|
|
startBluetoothScan()
|
|||
|
|
} else {
|
|||
|
|
updateStatus("蓝牙权限被拒绝,无法使用蓝牙功能")
|
|||
|
|
binding.bluetoothButton.text = "权限被拒绝"
|
|||
|
|
binding.bluetoothButton.setBackgroundColor(Color.parseColor("#9E9E9E"))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📋 权限清单
|
|||
|
|
|
|||
|
|
### AndroidManifest.xml
|
|||
|
|
```xml
|
|||
|
|
<!-- 蓝牙权限 -->
|
|||
|
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|||
|
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
|||
|
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
|||
|
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|||
|
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|||
|
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|||
|
|
|
|||
|
|
<!-- 蓝牙功能声明 -->
|
|||
|
|
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
|
|||
|
|
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### MainActivity.kt
|
|||
|
|
```kotlin
|
|||
|
|
companion object {
|
|||
|
|
private const val PERMISSION_REQUEST_CODE = 1001
|
|||
|
|
private val REQUIRED_PERMISSIONS = arrayOf(
|
|||
|
|
Manifest.permission.BLUETOOTH_SCAN,
|
|||
|
|
Manifest.permission.BLUETOOTH_CONNECT,
|
|||
|
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
|||
|
|
Manifest.permission.ACCESS_COARSE_LOCATION
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Used to load the 'cmake_project_test' library on application startup.
|
|||
|
|
init {
|
|||
|
|
System.loadLibrary("cmake_project_test")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🎯 最佳实践
|
|||
|
|
|
|||
|
|
### 1. 权限申请时机
|
|||
|
|
- 在用户主动点击蓝牙功能时申请
|
|||
|
|
- 避免应用启动时强制申请
|
|||
|
|
- 提供清晰的权限用途说明
|
|||
|
|
|
|||
|
|
### 2. 用户体验
|
|||
|
|
- 显示当前权限状态
|
|||
|
|
- 提供权限被拒绝时的解决方案
|
|||
|
|
- 友好的错误提示
|
|||
|
|
|
|||
|
|
### 3. 兼容性
|
|||
|
|
- 支持Android 6.0+的动态权限
|
|||
|
|
- 兼容Android 12+的新蓝牙权限
|
|||
|
|
- 处理权限被拒绝的情况
|
|||
|
|
|
|||
|
|
## 🔍 调试技巧
|
|||
|
|
|
|||
|
|
### 检查权限状态
|
|||
|
|
```kotlin
|
|||
|
|
private fun checkPermissionStatus(): String {
|
|||
|
|
val status = mutableListOf<String>()
|
|||
|
|
|
|||
|
|
for (permission in REQUIRED_PERMISSIONS) {
|
|||
|
|
val granted = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
|||
|
|
status.add("${permission.split(".").last()}: ${if (granted) "✓" else "✗"}")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return status.joinToString("\n")
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 日志输出
|
|||
|
|
- 权限申请过程会记录详细日志
|
|||
|
|
- 可通过Logcat查看权限状态变化
|
|||
|
|
- 便于调试权限相关问题
|
|||
|
|
|
|||
|
|
## 📝 注意事项
|
|||
|
|
|
|||
|
|
1. **位置权限必需**:蓝牙扫描需要位置权限,这是系统要求
|
|||
|
|
2. **权限持久性**:权限一旦授予,应用重启后仍然有效
|
|||
|
|
3. **系统限制**:某些系统可能限制权限申请
|
|||
|
|
4. **版本兼容**:不同Android版本的权限要求可能不同
|