PermissionJ
使用Aspect实现的面向切面进行Android动态权限申请
Github
使用:
- 根build.gradle
dependencies {
classpath "com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10"
}
- 需要申请权限的module build.gradle
plugins {
id 'kotlin-kapt'
}
// 选配
aspectjx {
// 需要织入代码的包名
include 'com.xxx'
// 不需要织入代码的包名
exclude 'com.xxx'
// 关闭AspectJX功能 enabled默认为true,即默认AspectJX生效
enabled true
}
dependencies {
implementation 'com.github.Archer1347:PermissionJ:1.0.0'
}
- 申请权限
@PermissionRequest(
permissions = [Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA],
requestCode = 1
)
fun test() {
Toast.makeText(this, "申请权限成功", Toast.LENGTH_SHORT).show()
}
- 申请权限失败(可选)
@PermissionRequestFailed
fun failed(permissionDetail: PermissionDetail) {
Toast.makeText(this, "申请权限失败", Toast.LENGTH_LONG).show()
}
或
@PermissionRequestFailed
fun failed(permissionDetail: PermissionDetail) {
Toast.makeText(
this, "申请权限失败\n" +
"请求码:${permissionDetail.requestCode}\n" +
"成功:${permissionDetail.grantedPermissions?.joinToString()}\n" +
"失败:${permissionDetail.deniedPermissions?.joinToString()}\n" +
"是否勾选了不再提示:${permissionDetail.rejectRemind}", Toast.LENGTH_LONG
).show()
}
原理:
- 使用registerForActivityResult进行权限申请
- aspect织入代码时,通过获取上下文activity,添加一个空的Fragment用于权限请求
- 兜底:如果获取不到activity,则打开一个透明Activity用于添加fragment
@Aspect
@DelicateCoroutinesApi
class PermissionAspect {
@Pointcut("execution(@com.permission.core.annotation.PermissionRequest * *(..))" + " && @annotation(permissionRequest)")
fun requestPermissionMethod(permissionRequest: PermissionRequest) {
}
@Around("requestPermissionMethod(permissionRequest)")
fun aroundJoinPoint(joinPoint: ProceedingJoinPoint, permissionRequest: PermissionRequest) {
if (hasSelfPermissions(application, *permissionRequest.permissions)) {
try {
joinPoint.proceed()
} catch (throwable: Throwable) {
Log.d(TAG, throwable.localizedMessage.orEmpty())
}
return
}
requestPermissions(joinPoint, permissionRequest)
}
/**
* Desc: 申请权限
*/
private fun requestPermissions(joinPoint: ProceedingJoinPoint, permissionRequest: PermissionRequest) {
GlobalScope.launch(Dispatchers.Main.immediate) {
// 优先获取当前activity
var activity = findActivityFromContext(joinPoint.`this`)
// 如果获取不到当前activity,则启动一个透明Activity
if (activity == null) {
// 启动activity,onCreate之后返回activity实例
activity = awaitStartActivityAndCreate(application, PermissionRequestActivity::class.java)
}
try {
// 申请权限,返回申请结果
val result = activity.requestPermissionsForResult(permissionRequest.permissions as Array<String>, permissionRequest.requestCode)
if (activity is PermissionRequestActivity) {
activity.finish()
}
// 如果没有权限被拒绝,则权限申请通过
if (result.deniedPermissions.isEmpty()) {
joinPoint.proceed()
} else {
onDenied(joinPoint, result)
}
} catch (throwable: Throwable) {
Log.e(TAG, throwable.localizedMessage.orEmpty())
}
}
}
/**
* Desc: 权限被拒绝,反射调用[PermissionRequestFailed]注解的方法,支持无参或只有一个[PermissionDetail]参数
*/
private fun onDenied(joinPoint: ProceedingJoinPoint, permissionDetail: PermissionDetail) {
val cls: Class<*> = joinPoint.`this`.javaClass
val methods = cls.declaredMethods
if (methods.isEmpty()) return
methods.firstOrNull {
it.isAnnotationPresent(PermissionRequestFailed::class.java)
}?.apply {
isAccessible = true
val types = parameterTypes
if (types.isEmpty()) {
invoke(joinPoint.`this`)
} else if (types.size == 1) {
invoke(joinPoint.`this`, permissionDetail)
}
}
}
/**
* Desc: 从切面上下文中获取当前Activity
*/
private fun findActivityFromContext(any: Any): FragmentActivity? {
if (any is Fragment) {
val activity = any.activity
if (activity != null && !activity.isDestroyed) {
return activity
}
}
if (any is FragmentActivity) {
return any
}
// 获取栈顶activity
val curActivity = getTopActivity()
if (curActivity is FragmentActivity) {
return curActivity
}
return null
}
}
利用Fragment进行权限申请
/**
* Desc: 使用registerForActivityResult进行权限申请
*
* @param permissions 权限列表
* @param requestCode 请求码
*
* @return 权限请求结果详情[PermissionDetail]
*/
internal suspend fun FragmentActivity.requestPermissionsForResult(permissions: Array<String>, requestCode: Int): PermissionDetail {
return suspendCoroutine { continuation ->
val fragment = Fragment()
var launch: ActivityResultLauncher<Array<String>>? = null
// 由于registerForActivityResult必须在fragment或者activity onCreate之前注册,所以这里每次都添加一个空fragment进行注册
launch = fragment.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (it.isEmpty()) {
launch?.unregister()
continuation.resumeWithException(IllegalArgumentException("权限${permissions.contentToString()}申请结果为空。注意:权限请求只有第一次有效,请检查是否有重复调用权限请求的地方"))
return@registerForActivityResult
}
// 授予权限列表
val grantedPermissions = mutableListOf<String>()
// 拒绝权限列表
val deniedPermissions = mutableListOf<String>()
// 是否拒绝且勾选了不再提示
var rejectRemind = false
it.forEach { entry ->
val permission = entry.key
if (!entry.value) {
deniedPermissions.add(permission)
if (!rejectRemind) {
rejectRemind = !ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
}
} else {
grantedPermissions.add(permission)
}
}
// 权限请求结果详情
val detail = PermissionDetail(
requestCode = requestCode,
grantedPermissions = grantedPermissions,
deniedPermissions = deniedPermissions,
rejectRemind = rejectRemind,
)
supportFragmentManager.beginTransaction().remove(fragment).commitAllowingStateLoss()
continuation.resume(detail)
}
supportFragmentManager.beginTransaction().add(fragment, "PermissionRequestFragment").commitAllowingStateLoss()
// 等待fragment创建完成
fragment.lifecycleScope.launchWhenResumed {
launch.launch(permissions)
}
}
}