Android EasyPermissions 封装实践:简化运行时权限申请流程
EasyPermissions 是 Google 官方推荐的权限处理库,但在实际项目中直接使用时,代码仍显冗长。本文介绍一种基于基类封装的方案,将权限申请逻辑收敛到统一入口,降低业务层的接入成本。
依赖配置
// build.gradle.kts
dependencies {
implementation("pub.devrel:easypermissions:3.0.0")
}
基类封装设计
核心思路是将权限回调、请求码管理、结果分发全部下沉到 BaseActivity,子类只需关注"权限通过后要做什么"。
abstract class BaseActivity : AppCompatActivity(),
EasyPermissions.PermissionCallbacks {
// 权限请求码枚举,避免魔法数字
protected enum class PermissionRequest(val code: Int) {
LOCATION(0x01),
CAMERA(0x02),
MICROPHONE(0x03),
BLUETOOTH(0x04)
}
// 简化后的回调接口,屏蔽底层细节
protected interface PermissionResultHandler {
fun onAcquired(requestCode: Int)
fun onRejected(requestCode: Int)
}
private var resultHandler: PermissionResultHandler? = null
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this
)
}
override fun onPermissionsGranted(requestCode: Int, perms: List<String>) {
resultHandler?.onAcquired(requestCode)
}
override fun onPermissionsDenied(requestCode: Int, perms: List<String>) {
resultHandler?.onRejected(requestCode)
}
// 权限申请入口
protected fun acquirePermissions(
rationale: String,
request: PermissionRequest,
vararg permissions: String,
handler: PermissionResultHandler
) {
this.resultHandler = handler
EasyPermissions.requestPermissions(
this,
rationale,
request.code,
*permissions
)
}
// 快速检查
protected fun checkGranted(vararg permissions: String): Boolean {
return EasyPermissions.hasPermissions(this, *permissions)
}
}
权限配置集中管理
将不同业务场景的权限清单抽离为独立方法,便于复用和维护。
// BaseActivity 扩展方法
protected fun locationPermissions(): Array<String> = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
protected fun bluetoothPermissions(): Array<String> = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE,
Manifest.permission.ACCESS_FINE_LOCATION
)
else -> arrayOf(
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.ACCESS_FINE_LOCATION
)
}
protected fun audioPermissions(): Array<String> = buildList {
add(Manifest.permission.RECORD_AUDIO)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
add(Manifest.permission.FOREGROUND_SERVICE)
}
}.toTypedArray()
业务层使用示例
以蓝牙场景为例,展示封装后的调用方式。
class DeviceScanActivity : BaseActivity() {
private fun ensureBluetoothThenScan() {
if (checkGranted(*bluetoothPermissions())) {
startBleScan()
return
}
acquirePermissions(
rationale = "扫描周边设备需要蓝牙和位置权限",
request = PermissionRequest.BLUETOOTH,
permissions = *bluetoothPermissions(),
handler = object : PermissionResultHandler {
override fun onAcquired(requestCode: Int) {
startBleScan()
}
override fun onRejected(requestCode: Int) {
Toast.makeText(this@DeviceScanActivity,
"权限被拒绝,无法扫描设备", Toast.LENGTH_SHORT).show()
}
}
)
}
private fun startBleScan() {
// 执行实际的蓝牙扫描逻辑
}
}
进阶:Fragment 场景适配
Fragment 需单独处理,但可复用同一套回调机制。
abstract class BaseFragment : Fragment(),
EasyPermissions.PermissionCallbacks {
protected fun requestFromFragment(
rationale: String,
requestCode: Int,
vararg permissions: String
) {
EasyPermissions.requestPermissions(
PermissionRequest.Builder(this, requestCode, *permissions)
.setRationale(rationale)
.build()
)
}
// 回调实现与 BaseActivity 保持一致...
}
注意事项
- 请求码需在 0-255 范围内,建议使用枚举集中管理
- Android 6.0 以下设备无需动态申请,但库内部已做兼容
- 权限被拒绝且勾选"不再询问"后,需引导用户前往设置页手动开启
- 从 Android 12 开始,蓝牙权限细分为
BLUETOOTH_SCAN、BLUETOOTH_CONNECT等,需按版本适配