1、概述
PermissionsDispatcher-github地址
项目中权限请求使用了 PermissionDispatcher 这个库,使用注解的方式进行权限请求。
观察其引入的依赖包含注解处理器,因此可以知道其在编译期为我们生成了代码。
1.1 基本使用
- 引入依赖 & 注解处理器
- 清单文件声明要使用权限
- 使用@RuntimePermissions修饰请求权限的 Fragment 或者 Activity
- 使用@NeedsPermission修饰请求权限的方法,说明需要的权限(数组形式)
- build 代码生成 XXXXPermissionDispatcher.kt 文件 (kotlin)
- 将请求权限的方法替换成 XXXWithPermissionCheck的拓展方法
- 重写 onRequestPermissionsResult 方法,调用生成的同名的拓展方法,处理获取到权限后的逻辑
2、源码分析
2.1、代码结构分析
-
- 注解
-
- 只有一个 PermissionUtils 类,共生成的代码中检查权限使用
-
- 注解处理器,用来生成java或者kotlin代码
从代码的组织结构可以就看出 PermissionDispatcher 工作主要就是依赖注解处理器生成的
XXXXPermissionDispatcher 文件,XXXX对应的用 @RuntimePermissions 修饰的类名称。(比如:
MainActivity对应的就是MainActivityPermissionDispatcher )。
2.2、kapt 生成代码
// This file was generated by PermissionsDispatcher. Do not modify!
@file:JvmName("MainActivity2PermissionsDispatcher")
package com.example.asmrapp
import androidx.core.app.ActivityCompat
import kotlin.Array
import kotlin.Int
import kotlin.IntArray
import kotlin.String
import permissions.dispatcher.PermissionUtils
private const val REQUEST_TAKECAMERA: Int = 0
private val PERMISSION_TAKECAMERA: Array<String> = arrayOf("android.permission.CAMERA")
fun MainActivity2.takeCameraWithPermissionCheck() {
if (PermissionUtils.hasSelfPermissions(this, *PERMISSION_TAKECAMERA)) {
takeCamera()
} else {
ActivityCompat.requestPermissions(this, PERMISSION_TAKECAMERA, REQUEST_TAKECAMERA)
}
}
fun MainActivity2.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
when (requestCode) {
REQUEST_TAKECAMERA ->
{
if (PermissionUtils.verifyPermissions(*grantResults)) {
takeCamera()
}
}
}
}
PermissionDispatcher 主要使用注解处理器并结合 JavaPoet 和 KotlinPoet 两个代码生成库来替我们生成代码(以Kotlin为例,它会为我们生成以上这样一个Kt文件,文件名取用@RuntimePermission注释的Activity或者Fragment的名称,并替其生成拓展方法,分别用来检查申请权限,以及处理获取权限后调用我们编写的代码的逻辑)。
2.3、注解处理器
class PermissionsProcessor : AbstractProcessor() {
private val javaProcessorUnits = listOf(JavaActivityProcessorUnit(),
JavaFragmentProcessorUnit())
private val kotlinProcessorUnits = listOf(KotlinActivityProcessorUnit(),
KotlinFragmentProcessorUnit())
/* Processing Environment helpers */
private var filer: Filer by Delegates.notNull()
override fun init(processingEnv: ProcessingEnvironment) {
super.init(processingEnv)
filer = processingEnv.filer
ELEMENT_UTILS = processingEnv.elementUtils
TYPE_UTILS = processingEnv.typeUtils
}
override fun getSupportedSourceVersion(): SourceVersion? {
return SourceVersion.latestSupported()
}
override fun getSupportedAnnotationTypes(): Set<String> {
return hashSetOf(RuntimePermissions::class.java.canonicalName)
}
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
// 1、
val requestCodeProvider = RequestCodeProvider()
// 2、获取被 @RuntimePermissions 注解修饰的类
roundEnv.getElementsAnnotatedWith(RuntimePermissions::class.java)
.sortedBy { it.simpleName.toString() }
.forEach {
val rpe = RuntimePermissionsElement(it as TypeElement)
val kotlinMetadata = it.getAnnotation(Metadata::class.java)
if (kotlinMetadata != null) {
// kotlin类
processKotlin(it, rpe, requestCodeProvider)
} else {
// Java类
processJava(it, rpe, requestCodeProvider)
}
}
return true
}
private fun processKotlin(element: Element, rpe: RuntimePermissionsElement, requestCodeProvider: RequestCodeProvider) {
val processorUnit = findAndValidateProcessorUnit(kotlinProcessorUnits, element)
val kotlinFile = processorUnit.createFile(rpe, requestCodeProvider)
kotlinFile.writeTo(filer)
}
private fun processJava(element: Element, rpe: RuntimePermissionsElement, requestCodeProvider: RequestCodeProvider) {
val processorUnit = findAndValidateProcessorUnit(javaProcessorUnits, element)
val javaFile = processorUnit.createFile(rpe, requestCodeProvider)
javaFile.writeTo(filer)
}
}
// 在units中查找第一个满足 type 是其 targetType 的子类型
fun <K> findAndValidateProcessorUnit(units: List<ProcessorUnit<K>>, element: Element): ProcessorUnit<K> {
val type = element.asType()
try {
return units.first { type.isSubtypeOf(it.getTargetType()) }
} catch (ex: NoSuchElementException) {
throw WrongClassException(type)
}
}
- 注解处理器的入口类 PermissionsProcessor
- process 入口方法中获取所有被 @RuntimePermissions 注解修饰的类
- 通过判断被修饰的类为Java类还是Kotlin类分别调用不同的方法进行处理
- 找到对应处理单元,调用 createFile 方法进行处理
- 这里其实就是 Activity 找 ActivityProcessorUnit,Fragment 找 FragmentProcessorUnit
class KotlinActivityProcessorUnit : KotlinBaseProcessorUnit() {
override fun getTargetType(): TypeMirror = typeMirrorOf("android.app.Activity")
override fun getActivityName(targetParam: String): String = targetParam
override fun addShouldShowRequestPermissionRationaleCondition(builder: FunSpec.Builder, permissionField: String, isPositiveCondition: Boolean) {
val condition = if (isPositiveCondition) "" else "!"
builder.beginControlFlow("if (%L%T.shouldShowRequestPermissionRationale(%L, *%N))", condition, permissionUtils, "this", permissionField)
}
override fun addRequestPermissionsStatement(builder: FunSpec.Builder, targetParam: String, permissionField: String, requestCodeField: String) {
builder.addStatement("%T.requestPermissions(%L, %N, %N)", ClassName("androidx.core.app", "ActivityCompat"), targetParam, permissionField, requestCodeField)
}
}
class KotlinFragmentProcessorUnit : KotlinBaseProcessorUnit() {
override fun getTargetType(): TypeMirror = typeMirrorOf("androidx.fragment.app.Fragment")
override fun getActivityName(targetParam: String): String = "$targetParam.requireActivity()"
override fun addShouldShowRequestPermissionRationaleCondition(builder: FunSpec.Builder, permissionField: String, isPositiveCondition: Boolean) {
val condition = if (isPositiveCondition) "" else "!"
builder.beginControlFlow("if (%L%T.shouldShowRequestPermissionRationale(%L, *%N))", condition, permissionUtils, "this" /* Fragment */, permissionField)
}
override fun addRequestPermissionsStatement(builder: FunSpec.Builder, targetParam: String, permissionField: String, requestCodeField: String) {
builder.addStatement("%L.requestPermissions(%L, %N)", targetParam, permissionField, requestCodeField)
}
}
- kotlin Activity 和 Fragment 对应的处理单元,Java 也有对应的代码
- 上面Activity和Fragment对应的处理单元都没有发现 createFile 方法,推测在公共的基类中
// 注解处理器生成代码的逻辑
override fun createFile(rpe: RuntimePermissionsElement, requestCodeProvider: RequestCodeProvider): FileSpec {
return FileSpec.builder(rpe.packageName, rpe.generatedClassName) // 包名、类名
.addComment(FILE_COMMENT) // 注释
.addAnnotation(createJvmNameAnnotation(rpe.generatedClassName)) // @file:JvmName 注解
.addProperties(createProperties(rpe, requestCodeProvider)) // 请求码 & 权限列表
.addFunctions(createWithPermissionCheckFuns(rpe))
.addFunctions(createOnShowRationaleCallbackFuns(rpe))
.addFunctions(createPermissionHandlingFuns(rpe))
.addTypes(createPermissionRequestClasses(rpe))
.build()
}
private fun createProperties(rpe: RuntimePermissionsElement, requestCodeProvider: RequestCodeProvider): List<PropertySpec> {
val properties = arrayListOf<PropertySpec>()
// 所有被 @NeedsPermission 修饰的方法
rpe.needsElements.sortedBy { it.simpleString() }.forEach {
// 添加两个属性
properties.add(createRequestCodeProp(it, requestCodeProvider.nextRequestCode()))
properties.add(createPermissionProperty(it))
if (it.parameters.isNotEmpty()) {
val hasOnRationaleParams = rpe.findOnRationaleForNeeds(it)?.parameters?.isNotEmpty()
?: true
if (hasOnRationaleParams) {
properties.add(createPendingRequestProperty(it))
} else {
properties.addAll(createArgProps(it))
}
}
}
return properties
}
// 生成请求码 (一次权限请求对应一个请求码,可以包含多个权限)
private fun createRequestCodeProp(e: ExecutableElement, index: Int): PropertySpec {
return PropertySpec.builder(requestCodeFieldName(e), Int::class.java, KModifier.CONST, KModifier.PRIVATE)
.initializer("%L", index)
.build()
}
// 与请求码对应的请求权限的列表
private fun createPermissionProperty(e: ExecutableElement): PropertySpec {
val permissionValue = e.getAnnotation(NeedsPermission::class.java).permissionValue()
val formattedValue = permissionValue.joinToString(
separator = ", ",
transform = { ""$it"" }
)
val parameterType = ARRAY.plusParameter(ClassName("kotlin", "String"))
return PropertySpec.builder(permissionFieldName(e), parameterType, KModifier.PRIVATE)
.initializer("arrayOf(%L)", formattedValue)
.build()
}
// 生成 XXXXWithPermissionCheck 函数的逻辑
private fun createWithPermissionCheckFuns(rpe: RuntimePermissionsElement): List<FunSpec> {
// 每个被 @NeedsPermission 修饰的方法
return rpe.needsElements.map { createWithPermissionCheckFun(rpe, it) }
}
private fun createWithPermissionCheckFun(rpe: RuntimePermissionsElement, method: ExecutableElement): FunSpec {
val builder = FunSpec.builder(withPermissionCheckMethodName(method))
.addOriginatingElement(rpe.element)
.addTypeVariables(rpe.ktTypeVariables)
.receiver(rpe.ktTypeName) // 拓展方法的接收者
if (method.enclosingElement.isInternal) {
builder.addModifiers(KModifier.INTERNAL)
}
method.parameters.forEach {
builder.addParameter(it.simpleString(), it.asPreparedType())
}
// 添加对应的方法体
addWithPermissionCheckBody(builder, method, rpe)
return builder.build()
}
- KotlinBaseProcessorUnit 的 createFile 方法
- FileSpec 是 kotlinpoet 中的类
生成方法体的函数有点长,单独列出来分析一下
fun requestCodeFieldName(e: ExecutableElement) =
"$GEN_REQUEST_CODE_PREFIX${e.simpleString().trimDollarIfNeeded().toUpperCase()}"
fun permissionFieldName(e: ExecutableElement) =
"$GEN_PERMISSION_PREFIX${e.simpleString().trimDollarIfNeeded().toUpperCase()}"
private fun addWithPermissionCheckBody(builder: FunSpec.Builder, needsMethod: ExecutableElement, rpe: RuntimePermissionsElement) {
// Create field names for the constants to use
// 请求码和权限的名称
val requestCodeField = requestCodeFieldName(needsMethod)
val permissionField = permissionFieldName(needsMethod)
// if maxSdkVersion is lower than os level does nothing
val maxSdkVersion = needsMethod.getAnnotation(NeedsPermission::class.java).maxSdkVersion
if (maxSdkVersion > 0) {
// Android 里面那个Build,可以用来获取SDK版本
builder.beginControlFlow("if (%T.VERSION.SDK_INT > %L)", build, maxSdkVersion)
.addCode(CodeBlock.builder()
.add("%N(", needsMethod.simpleString())
.add(varargsKtParametersCodeBlock(needsMethod))
.addStatement(")")
.addStatement("return")
.build())
.endControlFlow()
}
// Add the conditional for when permission has already been granted
val needsPermissionParameter = needsMethod.getAnnotation(NeedsPermission::class.java).value[0]
val activity = getActivityName()
/*
* if (PermissionUtils.hasSelfPermissions(this, *PERMISSION_TAKECAMERA)) {
* takeCamera()
* }
*/
// 前面是两个特殊权限的处理,一般为null,取?后面的内容,就是上面注释中if语句
addWithCheckBodyMap[needsPermissionParameter]?.addHasSelfPermissionsCondition(builder, activity, permissionField)
?: builder.beginControlFlow("if (%T.hasSelfPermissions(%L, *%N))", permissionUtils, activity, permissionField)
// 调用我们编写的方法
builder.addCode(CodeBlock.builder()
.add("%N(", needsMethod.simpleString())
.add(varargsKtParametersCodeBlock(needsMethod))
.addStatement(")")
.build()
)
builder.nextControlFlow("else")
// 其他注解的处理
// Add the conditional for "OnShowRationale", if present
val onRationale = rpe.findOnRationaleForNeeds(needsMethod)
val hasOnRationaleParams = onRationale?.parameters?.isNotEmpty() ?: true
val hasParameters = needsMethod.parameters.isNotEmpty()
if (hasParameters) {
if (hasOnRationaleParams) {
// If the method has parameters, precede the potential OnRationale call with
// an instantiation of the temporary Request object
val varargsCall = CodeBlock.builder()
.add("%N = %N(this, ",
pendingRequestFieldName(needsMethod),
permissionRequestTypeName(rpe, needsMethod)
)
.add(varargsKtParametersCodeBlock(needsMethod))
.addStatement(")")
builder.addCode(varargsCall.build())
} else {
needsMethod.parameters.forEach {
val code = CodeBlock.builder().addStatement("%N = %N", needsMethod.argumentFieldName(it), it.simpleString())
builder.addCode(code.build())
}
}
}
if (onRationale != null) {
addShouldShowRequestPermissionRationaleCondition(builder, permissionField)
if (hasParameters) {
if (hasOnRationaleParams) {
// For methods with parameters, use the PermissionRequest instantiated above
builder.addStatement("%N?.let { %N(it) }", pendingRequestFieldName(needsMethod), onRationale.simpleString())
} else {
builder.addStatement("%N()", onRationale.simpleString())
}
} else {
if (hasOnRationaleParams) {
// Otherwise, create a new PermissionRequest on-the-fly
builder.addStatement("%N(%N(this))", onRationale.simpleString(), permissionRequestTypeName(rpe, needsMethod))
} else {
builder.addStatement("%N()", onRationale.simpleString())
}
}
builder.nextControlFlow("else")
}
/*
* else {
* ActivityCompat.requestPermissions(this, PERMISSION_TAKECAMERA, REQUEST_TAKECAMERA)
* }
*/
addWithCheckBodyMap[needsPermissionParameter]?.addRequestPermissionsStatement(builder = builder, activityVar = getActivityName(), requestCodeField = requestCodeField)
?: addRequestPermissionsStatement(builder = builder, permissionField = permissionField, requestCodeField = requestCodeField)
if (onRationale != null) {
builder.endControlFlow()
}
builder.endControlFlow()
}
override fun addRequestPermissionsStatement(builder: FunSpec.Builder, targetParam: String, permissionField: String, requestCodeField: String) {
builder.addStatement("%T.requestPermissions(%L, %N, %N)", ClassName("androidx.core.app", "ActivityCompat"), targetParam, permissionField, requestCodeField)
}
- 如果当前系统SDK版本大于 @NeedsPermission 中指定的 maxSdkVersion 直接 return
- 生成检查是否已经获取权限的代码
- 其他非必要注解生成的代码
- 生成请求权限的代码
经过以上步骤就PermissionDispatcher就帮我们生成了权限检查和请求的代码逻辑。