PermissionJ
使用Aspect實現(xiàn)的面向切面進行Android動態(tài)權(quán)限申請
Github
使用:
- 根build.gradle
dependencies {
classpath "com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10"
}
- 需要申請權(quán)限的module build.gradle
plugins {
id 'kotlin-kapt'
}
// 選配
aspectjx {
// 需要織入代碼的包名
include 'com.xxx'
// 不需要織入代碼的包名
exclude 'com.xxx'
// 關(guān)閉AspectJX功能 enabled默認為true,即默認AspectJX生效
enabled true
}
dependencies {
implementation 'com.github.Archer1347:PermissionJ:1.0.0'
}
- 申請權(quán)限
@PermissionRequest(
permissions = [Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA],
requestCode = 1
)
fun test() {
Toast.makeText(this, "申請權(quán)限成功", Toast.LENGTH_SHORT).show()
}
- 申請權(quán)限失敗(可選)
@PermissionRequestFailed
fun failed(permissionDetail: PermissionDetail) {
Toast.makeText(this, "申請權(quán)限失敗", Toast.LENGTH_LONG).show()
}
或
@PermissionRequestFailed
fun failed(permissionDetail: PermissionDetail) {
Toast.makeText(
this, "申請權(quán)限失敗\n" +
"請求碼:${permissionDetail.requestCode}\n" +
"成功:${permissionDetail.grantedPermissions?.joinToString()}\n" +
"失敗:${permissionDetail.deniedPermissions?.joinToString()}\n" +
"是否勾選了不再提示:${permissionDetail.rejectRemind}", Toast.LENGTH_LONG
).show()
}
原理:
- 使用registerForActivityResult進行權(quán)限申請
- aspect織入代碼時境蔼,通過獲取上下文activity挥吵,添加一個空的Fragment用于權(quán)限請求
- 兜底:如果獲取不到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: 申請權(quán)限
*/
private fun requestPermissions(joinPoint: ProceedingJoinPoint, permissionRequest: PermissionRequest) {
GlobalScope.launch(Dispatchers.Main.immediate) {
// 優(yōu)先獲取當前activity
var activity = findActivityFromContext(joinPoint.`this`)
// 如果獲取不到當前activity哺徊,則啟動一個透明Activity
if (activity == null) {
// 啟動activity构资,onCreate之后返回activity實例
activity = awaitStartActivityAndCreate(application, PermissionRequestActivity::class.java)
}
try {
// 申請權(quán)限芜壁,返回申請結(jié)果
val result = activity.requestPermissionsForResult(permissionRequest.permissions as Array<String>, permissionRequest.requestCode)
if (activity is PermissionRequestActivity) {
activity.finish()
}
// 如果沒有權(quán)限被拒絕,則權(quán)限申請通過
if (result.deniedPermissions.isEmpty()) {
joinPoint.proceed()
} else {
onDenied(joinPoint, result)
}
} catch (throwable: Throwable) {
Log.e(TAG, throwable.localizedMessage.orEmpty())
}
}
}
/**
* Desc: 權(quán)限被拒絕庐冯,反射調(diào)用[PermissionRequestFailed]注解的方法孽亲,支持無參或只有一個[PermissionDetail]參數(shù)
*/
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進行權(quán)限申請
/**
* Desc: 使用registerForActivityResult進行權(quán)限申請
*
* @param permissions 權(quán)限列表
* @param requestCode 請求碼
*
* @return 權(quán)限請求結(jié)果詳情[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("權(quán)限${permissions.contentToString()}申請結(jié)果為空展父。注意:權(quán)限請求只有第一次有效返劲,請檢查是否有重復調(diào)用權(quán)限請求的地方"))
return@registerForActivityResult
}
// 授予權(quán)限列表
val grantedPermissions = mutableListOf<String>()
// 拒絕權(quán)限列表
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)
}
}
// 權(quán)限請求結(jié)果詳情
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創(chuàng)建完成
fragment.lifecycleScope.launchWhenResumed {
launch.launch(permissions)
}
}
}