Android 自定义注解处理器

本文详细介绍了如何在Java或Kotlin中定义一个针对类构造方法的DemoAnnotation注解,并创建一个注解处理器,用于根据该注解动态生成工厂类。通过实例演示,展示了如何在项目中使用并查看生成的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        之前我们可能用过dagger、hilt之类的注解,使用这些注解可以方便我们的工作,减少我们的代码编写量。因此,本文主要是介绍如何自定义一个注解处理器。可以分为2个部分,一、定义注解和注解处理器;二、注解使用演示。

        本文的目标:定义一个类构造方法上的注解DemoAnnotation,并使用此注解生成一个工厂类。注解的执行结果如下图所示

一、注解和注解处理器

        首先我们要新建一个”Java or Kotlin Library“组件项目 ,然后 对此子项目的build.gradle文件进行配置,如下:

plugins {
    id 'java-library'
    id 'kotlin'
    id 'kotlin-kapt'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.31"
    kapt 'com.google.auto.service:auto-service:1.0-rc6'
    compileOnly 'com.google.auto.service:auto-service:1.0-rc6'
}

        1、定义注解

        我们定义一个在类构造方法上的注解,如下所示:

package com.example.demoprocessor

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CONSTRUCTOR)
annotation class DemoAnnotation

        2、定义注解处理器

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({DemoProcessor.DEMO_ANNOTATION})
public class DemoProcessor extends AbstractProcessor {
    static final String DEMO_ANNOTATION = "com.example.demoprocessor.DemoAnnotation";
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return new DemoAnnotationProcessor().process(annotations, roundEnv, processingEnv);
    }
}

        我们需要使用@SupportedAnnotationTypes指明注解的名称(包名+注解名),然后使用@AutoService注解,作用是可以帮我们在注解路径里自动添加当前的注解类。

        本文注解处理器的实现放在DemoAnnotationProcessor类中,如下所示:

package com.example.demoprocessor

import java.io.IOException
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.PackageElement
import javax.lang.model.element.TypeElement
import javax.tools.JavaFileObject

class DemoAnnotationProcessor {

    fun process(
        annotations: Set<TypeElement>,
        roundEnv: RoundEnvironment,
        processingEnv: ProcessingEnvironment
    ): Boolean {
        println("start process")
        if (roundEnv.rootElements.size == 0) {
            return false
        }
        for (annotation in annotations) {
            // 留下需要的注解
            if (!annotation.qualifiedName.toString().contains(DEMO_ANNOTATION)) {
                continue
            }
            val elements = roundEnv.getElementsAnnotatedWith(annotation)
            for (element in elements) {
                // 去掉非方法的注解
                if (element !is ExecutableElement) {
                    continue
                }
                // 检查被注解的方法是否符合要求
                if (!checkHasNoErrors(element)) {
                    continue
                }
                // 获取类
                val classElement = element.enclosingElement as TypeElement
                // 获取包
                val packageElement = getPackageElement(classElement)
                createServiceClass(
                    packageElement!!.qualifiedName.toString(),
                    classElement.simpleName.toString(),
                    classElement.simpleName.toString(),
                    processingEnv
                )
            }
        }
        return true
    }

    private fun createServiceClass(
        pkName: String,
        simpleClazzName: String,
        serviceName: String,
        processingEnv: ProcessingEnvironment
    ) {
        println("createServiceClass")
        val className = "$pkName.$simpleClazzName"
        val builder = StringBuilder()
            .append("package com.example.annotationprocessor.generated;\n\n")
            .append("import $className;\n\n")
            .append("public class GeneratedClass$serviceName {\n\n") // open class
            .append("\tpublic $simpleClazzName getInstance() {\n") // open method
            .append("\t\treturn ")
        builder.append("new $simpleClazzName()")
        builder.append(";\n") // end return
            .append("\t}\n") // close method
            .append("}\n") // close class
        try { // write the file
            val source: JavaFileObject = processingEnv.filer
                .createSourceFile(
                    "com.example.annotationprocessor.generated.GeneratedClass"
                            + serviceName
                )
            val writer = source.openWriter()
            writer.write(builder.toString())
            writer.flush()
            writer.close()
        } catch (e: IOException) {
            // Note: calling e.printStackTrace() will print IO errors
            // that occur from the file already existing after its first run, this is normal
        }
    }

    private fun getPackageElement(subscriberClass: TypeElement): PackageElement? {
        var candidate = subscriberClass.enclosingElement
        while (candidate !is PackageElement) {
            candidate = candidate.enclosingElement
        }
        return candidate
    }

    private fun checkHasNoErrors(initMethod: ExecutableElement): Boolean {
        if (!initMethod.modifiers.contains(Modifier.PUBLIC)) {
            return false
        }
        return initMethod.parameters.size <= 0
    }

    private final val DEMO_ANNOTATION = "com.example.demoprocessor.DemoAnnotation"
}

        注意在此过程中,只能创建文件,而不能修改已经存在的文件。

二、注解的使用

        首先,我们需要在app项目的build.gradle里添加对注解处理的依赖。注意,要使用kapt或annotationProcessor添加对注解处理器的依赖,然后使用implement 添加对注解的依赖。因为本文将注解和注解处理器都放在了一个组件里,所以会用kapt和implement对同一组件添加依赖,如下所示:

    implementation project(':demoprocessor')
    kapt project(":demoprocessor")

        在添加完成之后,就可以使用注解了,如下:

class TestAnnotation @DemoAnnotation constructor(){

    init {
        // 模拟初始化操作
        var i = 0
        i++
    }
}

        最后,在build之后,生成的注解文件如开篇的第一张图所示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值