XXPermissions与Kotlin扩展:简化权限请求代码
你是否还在为Android权限申请的繁琐代码而头疼?是否觉得Java版本的权限请求逻辑冗长且可读性差?本文将带你探索如何通过Kotlin扩展功能,结合XXPermissions框架,用更简洁、更优雅的方式处理Android权限请求,让你的代码量减少50%以上,同时提升可读性和可维护性。读完本文后,你将能够:掌握XXPermissions的核心API使用、编写自定义Kotlin扩展函数、处理复杂权限场景的最佳实践。
框架简介与核心优势
XXPermissions是一款专为Android平台设计的权限请求框架,已全面适配至Android 14,提供了简洁的API和强大的功能。相比其他权限框架,它具有以下核心优势:
- 全面的权限支持:覆盖从普通危险权限到特殊权限(如悬浮窗、安装包、画中画等)的所有权限类型,具体可参考README.md中的权限对比表格。
- 自动版本适配:新权限在旧系统上自动适配,无需开发者手动判断Android版本。
- 错误检测机制:在Debug模式下自动检测常见的权限申请错误,如未在Manifest中注册权限、TargetSdkVersion不匹配等。
- 内存泄漏修复:针对Android 12及以上系统的权限申请内存泄漏问题进行了专门优化。
图1:XXPermissions支持的多种权限请求场景,包括单权限、组权限等
Kotlin扩展的力量:从繁琐到简洁
Kotlin的扩展函数(Extension Functions)允许我们为现有类添加新的函数,而无需继承该类或使用设计模式(如装饰器模式)。这一特性非常适合简化XXPermissions的调用方式。
标准用法对比
Java标准用法:
XXPermissions.with(this)
.permission(PermissionLists.getCameraPermission())
.permission(PermissionLists.getRecordAudioPermission())
.request(new OnPermissionCallback() {
@Override
public void onResult(@NonNull List<IPermission> grantedList, @NonNull List<IPermission> deniedList) {
if (deniedList.isEmpty()) {
// 权限全部授予
startCamera();
} else {
// 处理权限被拒绝的情况
showPermissionDeniedTip(deniedList);
}
}
});
Kotlin扩展后的用法:
// 扩展函数定义
fun Activity.requestCameraAndRecordPermissions(
onGranted: () -> Unit,
onDenied: (List<IPermission>) -> Unit
) {
XXPermissions.with(this)
.permission(PermissionLists.getCameraPermission())
.permission(PermissionLists.getRecordAudioPermission())
.request(object : OnPermissionCallback {
override fun onResult(grantedList: MutableList<IPermission>, deniedList: MutableList<IPermission>) {
if (deniedList.isEmpty()) {
onGranted()
} else {
onDenied(deniedList)
}
}
})
}
// 调用处代码
requestCameraAndRecordPermissions(
onGranted = { startCamera() },
onDenied = { showPermissionDeniedTip(it) }
)
通过对比可以明显看出,使用Kotlin扩展后,调用处的代码变得极为简洁,将重复的模板代码封装到扩展函数中,大幅提升了代码可读性。
核心API与扩展实现
基础扩展函数编写
要创建有效的Kotlin扩展,首先需要熟悉XXPermissions的核心API。XXPermissions的入口类是library/src/main/java/com/hjq/permissions/XXPermissions.java,主要提供以下关键方法:
with(activity: Activity): 创建权限请求实例permission(permission: IPermission): 添加需要请求的权限request(callback: OnPermissionCallback): 发起权限请求
基于这些API,我们可以编写基础的扩展函数。例如,为Activity添加一个通用的权限请求扩展:
// 通用权限请求扩展
fun Activity.requestPermissions(
vararg permissions: IPermission,
onAllGranted: () -> Unit,
onDenied: (List<IPermission>) -> Unit,
onDoNotAskAgain: (List<IPermission>) -> Unit = {
// 默认实现,可根据需求重写
XXPermissions.startPermissionActivity(this, it)
}
) {
XXPermissions.with(this)
.permission(permissions.toList())
.request(object : OnPermissionCallback {
override fun onResult(grantedList: MutableList<IPermission>, deniedList: MutableList<IPermission>) {
when {
deniedList.isEmpty() -> onAllGranted()
XXPermissions.isDoNotAskAgainPermissions(this@requestPermissions, deniedList) ->
onDoNotAskAgain(deniedList)
else -> onDenied(deniedList)
}
}
})
}
这个扩展函数封装了权限请求的三种常见结果:全部授予、部分拒绝、被勾选"不再询问"。使用时只需关注具体的业务逻辑,无需重复编写权限请求模板代码。
常见权限场景的扩展实现
针对不同的权限使用场景,我们可以编写更具体的扩展函数,使代码更加简洁。
1. 相机权限请求
相机权限是应用中常见的权限需求,通常还需要同时请求录音权限(如视频拍摄场景)。我们可以创建一个专用的扩展函数:
// 相机和录音权限扩展
fun Activity.requestCameraAndAudioPermissions(
onGranted: () -> Unit,
onDenied: (List<IPermission>) -> Unit,
onDoNotAskAgain: (List<IPermission>) -> Unit
) {
requestPermissions(
PermissionLists.getCameraPermission(),
PermissionLists.getRecordAudioPermission(),
onAllGranted = onGranted,
onDenied = onDenied,
onDoNotAskAgain = onDoNotAskAgain
)
}
// 使用示例
requestCameraAndAudioPermissions(
onGranted = { startVideoRecording() },
onDenied = { showToast("需要相机和录音权限才能录制视频") },
onDoNotAskAgain = { permissions ->
showDialog("请在应用设置中开启相机和录音权限",
positiveAction = { XXPermissions.startPermissionActivity(this, permissions) })
}
)
图2:使用XXPermissions请求相机权限的界面效果
2. 存储权限请求
存储权限在不同Android版本中有较大差异,XXPermissions已经处理了这些差异,我们可以通过扩展函数进一步简化:
// 存储权限请求扩展
fun Activity.requestStoragePermissions(
onGranted: () -> Unit,
onDenied: (List<IPermission>) -> Unit
) {
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
PermissionLists.getManageExternalStoragePermission()
} else {
PermissionLists.getWriteExternalStoragePermission()
}
requestPermissions(
storagePermission,
onAllGranted = onGranted,
onDenied = onDenied
)
}
这个扩展函数自动根据系统版本选择合适的存储权限,对于Android 11及以上使用MANAGE_EXTERNAL_STORAGE,以下版本使用WRITE_EXTERNAL_STORAGE。XXPermissions会自动处理版本适配,开发者无需关心底层实现细节。
图3:Android 11+系统的存储权限请求界面
高级扩展技巧
1. 协程支持
结合Kotlin协程,我们可以将回调式的权限请求转换为 suspend 函数,进一步提升代码可读性。首先需要创建一个协程适配扩展:
// 协程支持扩展
suspend fun Activity.awaitPermissions(
vararg permissions: IPermission
): PermissionResult {
return suspendCancellableCoroutine { continuation ->
requestPermissions(
*permissions,
onAllGranted = {
continuation.resume(PermissionResult.Granted)
},
onDenied = { denied ->
continuation.resume(PermissionResult.Denied(denied))
},
onDoNotAskAgain = { denied ->
continuation.resume(PermissionResult.DoNotAskAgain(denied))
}
)
}
}
// 权限结果密封类
sealed class PermissionResult {
object Granted : PermissionResult()
data class Denied(val permissions: List<IPermission>) : PermissionResult()
data class DoNotAskAgain(val permissions: List<IPermission>) : PermissionResult()
}
使用时,在ViewModel或协程作用域中调用:
viewModelScope.launch {
when (val result = requireActivity().awaitPermissions(
PermissionLists.getCameraPermission()
)) {
is PermissionResult.Granted -> startCamera()
is PermissionResult.Denied -> showDeniedMessage(result.permissions)
is PermissionResult.DoNotAskAgain -> showSettingDialog(result.permissions)
}
}
2. 自定义权限描述
XXPermissions支持自定义权限申请前的说明对话框,通过扩展函数可以轻松实现:
// 带权限说明的扩展
fun Activity.requestPermissionWithRationale(
permission: IPermission,
rationale: String,
onGranted: () -> Unit,
onDenied: () -> Unit
) {
if (XXPermissions.isGrantedPermission(this, permission)) {
onGranted()
return
}
if (XXPermissions.shouldShowRequestPermissionRationale(this, permission)) {
AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage(rationale)
.setPositiveButton("确定") { _, _ ->
requestPermissions(permission,
onAllGranted = onGranted,
onDenied = { onDenied() })
}
.setNegativeButton("取消", null)
.show()
} else {
requestPermissions(permission,
onAllGranted = onGranted,
onDenied = { onDenied() })
}
}
使用时,只需提供权限和对应的说明文本:
requestPermissionWithRationale(
permission = PermissionLists.getLocationPermission(),
rationale = "为了提供精准的位置服务,需要获取您的位置信息",
onGranted = { startLocationService() },
onDenied = { showLocationDeniedTip() }
)
最佳实践与注意事项
1. 权限结果处理
权限请求的结果处理需要考虑多种情况,包括:权限全部授予、部分授予、被拒绝、被勾选"不再询问"等。通过Kotlin扩展,我们可以统一处理这些情况,确保代码的一致性。建议的做法是创建一个基础的权限结果处理扩展:
// 权限结果处理扩展
fun Activity.handlePermissionResult(
grantedList: List<IPermission>,
deniedList: List<IPermission>,
onAllGranted: () -> Unit,
onPartialGranted: (List<IPermission>, List<IPermission>) -> Unit = { _, _ -> },
onDenied: (List<IPermission>) -> Unit = {
Toast.makeText(this, "部分权限被拒绝,功能可能受限", Toast.LENGTH_SHORT).show()
},
onDoNotAskAgain: (List<IPermission>) -> Unit = { permissions ->
AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage("需要以下权限才能正常使用功能:${permissions.joinToString { it.name }},请在设置中开启")
.setPositiveButton("去设置") { _, _ ->
XXPermissions.startPermissionActivity(this, permissions)
}
.setNegativeButton("取消", null)
.show()
}
) {
when {
deniedList.isEmpty() -> onAllGranted()
XXPermissions.isDoNotAskAgainPermissions(this, deniedList) -> onDoNotAskAgain(deniedList)
else -> {
onPartialGranted(grantedList, deniedList)
onDenied(deniedList)
}
}
}
2. 避免内存泄漏
在使用Kotlin扩展时,需要注意避免常见的内存泄漏问题:
- 避免在扩展函数中持有Activity的强引用,特别是在回调中。
- 对于需要在Fragment中使用的扩展,建议同时提供Fragment版本的扩展函数。
- 当Activity或Fragment销毁时,取消正在进行的权限请求。
3. 与ViewModel结合使用
在MVVM架构中,建议将权限请求的逻辑放在ViewModel中处理,通过LiveData或StateFlow将结果通知给UI层。可以创建专门的扩展函数支持ViewModel:
// ViewModel扩展
fun ViewModel.requestPermissions(
activity: Activity,
vararg permissions: IPermission,
result: MutableLiveData<PermissionResult>
) {
XXPermissions.with(activity)
.permission(permissions.toList())
.request(object : OnPermissionCallback {
override fun onResult(grantedList: MutableList<IPermission>, deniedList: MutableList<IPermission>) {
when {
deniedList.isEmpty() -> result.value = PermissionResult.Granted
XXPermissions.isDoNotAskAgainPermissions(activity, deniedList) ->
result.value = PermissionResult.DoNotAskAgain(deniedList)
else -> result.value = PermissionResult.Denied(deniedList)
}
}
})
}
总结与展望
通过Kotlin扩展功能,XXPermissions的使用体验得到了极大的提升,代码量显著减少,可读性和可维护性大幅提高。本文介绍的扩展技巧可以应用于各种权限请求场景,从简单的单权限请求到复杂的多权限组合场景。
XXPermissions框架仍在持续发展中,未来可能会加入更多高级功能,如权限请求的优先级管理、动态权限组等。作为开发者,我们应该充分利用Kotlin的语言特性,结合框架提供的API,编写更加简洁、高效的代码。
最后,建议大家在实际项目中根据具体需求,创建一套适合自己团队的权限扩展函数库,统一权限请求的处理方式,提升开发效率和代码质量。如果你有更好的扩展实践或想法,欢迎在项目的Issue中分享交流。
本文示例代码基于XXPermissions 26.5版本编写,不同版本间可能存在API差异,请以实际使用的版本为准。完整的权限扩展示例可参考项目的帮助文档。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






