IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 自定义注解处理器 -> 正文阅读

[移动开发]Android 自定义注解处理器

????????之前我们可能用过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之后,生成的注解文件如开篇的第一张图所示。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-06 13:56:29  更:2022-02-06 13:57:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:55:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码