Android 开发之 自定义注解处理器

2,525 阅读2分钟
  1. 新建两个 java lib 项目,分别放置 注解(工程名称为myAnnotation)和注解处理器(工程名称为 Compiler)和一个Android工程

注解

  1. 再工程 myAnnotation 新建注解

java 版本

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface One {}

Kotlin 版本

@Target(AnnotationTarget.CLASS)
@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
annotation class Two

如果是 kotlin 版本 需要 再 build.gradle 里面 增加 以下代码 来让工程支持 kotlin的语法

apply plugin: 'kotlin'
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

注解处理器

说在前面
  1. 注解处理器(compiler)工程main 的目录结构是kotlin还是 java 包 不影响最终结果
  2. 需要再main 底下新建package,名称为 resources
  3. resources底下新建文件 META-INF/services/javax.annotation.processing.Processor
  4. javax.annotation.processing.Processor 下写入 注解处理器的全名称 eg: com.example.annotationtwo.TwoProcessor

编写 注解处理器

  1. 如果需要支持kotlin 则 与 myannotation支持的方式相同,再 build.gradle 增加代码即可
  2. 因为注解处理器需要使用到 注解 所以需要再 build.gradle 导入 刚才的注解库
implementation project(':myannotation')
  1. 编写处理器
@SupportedAnnotationTypes("com.example.annotationtwo.Two")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
class TwoProcessor2 : AbstractProcessor() {
    var messager: Messager? = null

    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        messager = processingEnv.messager
        messager?.printMessage(Diagnostic.Kind.WARNING, "newbie:init")
    }

    override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
        messager!!.printMessage(Diagnostic.Kind.WARNING, "TwoProcessor2 process")
        return false
    }
}

特别请注意,如果 printMessage的类型为Diagnostic.Kind.Error 则编译会直接报错,所以,这里只能采用WARNING或者 MANDATORY_WARNING 因为gradle版本的关系,据说有问题,本人 gradle 版本 ext.kotlin_version = '1.3.61' 不可以 使用其他 类型进行打印, Diagnostic.Kind.NOTE 是不会输出日志的 3. process 的返回值决定是否只在注解发生改变的时候重新编译,如果是true的话,则只在改变的时候重新编译。 4. SupportedAnnotationTypes源自于 父类 override fun getSupportedAnnotationTypes(): MutableSet<String> 表明该注解处理器可以支持解释哪些注解类,在 kotlin 里面可以直接写成注解处理器的注解即可@SupportedAnnotationTypes("com.example.annotationtwo.Two") 注意入参为 全名称,如果是多个则直接后面继续增加即可 @SupportedAnnotationTypes("","") 5. SupportedSourceVersion 也是源自父类 override fun getSupportedSourceVersion(): SourceVersion 表明支持的jdk的版本 如果低于8 的版本会报 警告 来自注释处理程序 'org.jetbrains.kotlin.kapt3.base.ProcessorWrapper' 的受支持 source 版本 'RELEASE_7' 低于 -source '1.8' 6. 如果需要携带参数给到 注解处理器,则 需要重写 override fun getSupportedOptions(): MutableSet<String>

// 设置支持的指令
 override fun getSupportedOptions(): MutableSet<String> = hashSetOf("myoption")
 override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        messager = processingEnv.messager
        // 取出这个值
        val myoption = processingEnv.options["myoption"]
        messager?.printMessage(Diagnostic.Kind.WARNING, "newbie:$myoption")
    }

参数的传入处,就在 自己的android项目(上面是java lib项目)的build.gradle 中(下面会说) 7. 如果注解处理器包含中文注释,则需要再 build.gradle 中新增

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

使用注解处理器

  1. 新建Android工程
  2. Android工程项目 的build.gradle 中 可以根据自己工程语言的采用(kotlin 还是 java)来选择不同的导入方式(kapt 还是annotationProcessor) 如果是kapt 请注意需要导入 apply plugin: 'kotlin-kapt' annotationProcessor 再gradle 2.2.0之后就自带了,所以不需要导入任何的插件即可使用
apply plugin: 'kotlin-kapt'

dependencies {
    kapt project(":Compiler")
    implementation project(':myannotation')
}

或者 java 版版本

dependencies {
    annotationProcessor project(":Compiler")
    implementation project(':myannotation')
}
  1. 如果需要传递数据给到注解处理器
android {
        ...
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    argument "myoption", "value1"
                    argument "key2", "value2"
                }
            }
        }
    }

注意事项

  1. 错误 :org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:kaptDebugKotlin'

可能的原因之一: 再使用了kotlin的项目里面导入了kotlin-kapt的插件,还是用 annotationProcessor 应该改为 kapt 即可

可能的原因之二: 再 implementation 的工程中,有 注解处理器的存在, 应该 : 将注解和注解处理器分成两个不同的java lib库,kapt 导入 注解处理器的lib ,注解的库 使用 implemention 进行导入

可能的原因之三:再注解处理器 的库里面打印日志使用的是 ERROR,如果采用Error,会抛异常, 跟 throw exception("message")是一个道理 , kapt 支支持两种方式的打印 Diagnostic.Kind.MANDATORY_WARNINGDiagnostic.Kind.WARNING