Android组件化设计2 --- 自定义注解处理器(APT)

先把图放上来
Android组件化设计2 --- 自定义注解处理器(APT)_第1张图片
这是Android组件化设计 — Gradle的神奇之处中的关于组件化的架构图,其中,组件1 … 组件4之前是不能相互依赖的,那么如果组件1想要调起组件2,只能通过基础层组件的路由能力。

在组件之前没有依赖的情况下,相互调用,那么可以通过类加载的方法,例如app壳想要调起注册模块,那么可以拿到注册模块的RegisterActivity的全类名,通过类加载的方式启动

fun jump(view: View) {
	//com.study.register.RegisterActivity
	//
    val RegisterActivity = Class.forName("com.study.register.RegisterActivity")

    startActivity(Intent(this,RegisterActivity))
}

或者可以通过注册路由表的方式,将所有的Activity的类信息注册到全局的Map中,如果存在成百的Actiivty,需要每个Activity手动去注册。

那么有没有一种方式,在编译的过程中就能拿到全部的类信息,注册到路由表中,避免手动注册这种方式

组件化路由基础 APT

    • 1 定义路由注解
    • 2 注解处理器 APT Compiler
    • 3 注解处理前的初始化配置
    • 踩坑:创建的注解处理器没有工作

APT是注解处理工具,用于编译期在源代码中查找注解,根据注解生成新的类 函数;像Eventus、ButterKnife、ARouter都是编译期生成新的类,像EventBus是采用传统的生成代码方式,直接写字符串生成

传统方式

//1 先写包名 直接write
package com.study.modulelization
//2 再写类名
class HttpConfig {
	//3 如果有方法 就再写方法名
    companion object{

        private const val DEBUG = "http://106.108.2.3//test-baidu.com"
        private const val RELEASE = "http://106.108.2.3//baidu.com"
    }
}

而像ButterKnife、ARouter采用的是JavaPoet方式,引入了面向对象的思想,跟传统方式相反,不如传统方式的可读性强

1 定义路由注解

@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.TYPE)
annotation class LayRouter(
    val group:String,
    val path:String = ""
)

Kotlin中的注解,跟Java中的注解类似,在自定义注解时,需要声明元注解;

其中Retention代表注解存在的时期,SOURCE(源码时期)、BINARY(编译期,跟Java区分,Java编译期为Class)、RUNTIME(运行时)

Target为声明作用的对象,TYPE(类、接口等)、FUNCTION(函数,但不包含构造方法)、CONSTRUCTOR(构造方法)、PROPERTY_GETTER / PROPERTY_SETTER(get set方法)、FIELD(属性)

2 注解处理器 APT Compiler

Android组件化设计2 --- 自定义注解处理器(APT)_第2张图片

在声明注解之后,需要注解处理器来处理注解,在系统编译时,会从全部的代码中查找自定义的注解,生成代码,那么注解处理器相当于一个服务,在后台运行,检测代码,因此需要做一些配置

layout_compiler # build.gradle

dependencies {
	//背后的服务
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
	//JavaPoet
    implementation "com.squareup:javapoet:1.13.0"
	//要处理的注解
    implementation project(path: ':lay_router')
    
}

谷歌的AutoService可以在编译时在后台检测代码,查找自定义的注解

3 注解处理前的初始化配置

@AutoService(Processor::class)
class AnnotationProcessor : AbstractProcessor() {

    //操作Element的工具类(类、函数、属性等)
    private var elementTool:Elements? = null
    //类信息操作类 继承  实现 等实现方式
    private var typeTool:Types? = null
    //日志打印
    private var message:Messager? = null
    //文件输出
    private var filer:Filer? = null
	//支持的注解类型,就是我们自定义的注解
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(LayRouter::class.java.canonicalName)
    }
	//支持的Java版本 Java 1.8
    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.RELEASE_8
    }
	//支持的参数选项,就是从app壳传递到注解处理器的参数
    override fun getSupportedOptions(): MutableSet<String> {
        return mutableSetOf("moduleName")
    }

    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)

        elementTool = processingEnv!!.elementUtils
        typeTool = processingEnv.typeUtils
        message = processingEnv.messager
        filer = processingEnv.filer

        message!!.printMessage(Diagnostic.Kind.NOTE,"init 注解处理器初始化")

        val s = processingEnv.options["moduleName"]
        message!!.printMessage(Diagnostic.Kind.NOTE,"--------->$s")
    }


    //处理注解
    override fun process(p0: MutableSet<out TypeElement>?, p1: RoundEnvironment?): Boolean {
    
        return false
    }
}

AbstractProcessor是注解处理器,后续会有一个专门的章节介绍,配合编译的过程。

当一个类被注解修饰之后,AnnotationProcessor需要拿到这个类的信息以便于生成代码,因此需要创建一些能够获取类信息的工具,ProcessingEnvironment中,存在我们想要的工具


public interface ProcessingEnvironment {
    Map<String, String> getOptions();
	//日志打印
    Messager getMessager();
	//文件生成
    Filer getFiler();
	//类 接口 属性的操作,都可以看做一个元素
    Elements getElementUtils();
	//类信息的操作
    Types getTypeUtils();
	//获取版本信息
    SourceVersion getSourceVersion();

    Locale getLocale();
}

在app壳工程中,使用注解,那么在注解处理器中(lay_compiler)怎么拿到app壳工程的参数信息呢,那么在app壳中通过Gradle向注解处理器传递参数
Android组件化设计2 --- 自定义注解处理器(APT)_第3张图片
现在是Kotlin的工程,在传参的时候通过kapt — arguments — arg传递参数

 kapt {
    arguments{
        arg("moduleName","zhijiedamamamfamfafafafa")
    }
}

AnnotationProcessor # init

override fun init(processingEnv: ProcessingEnvironment?) {
    super.init(processingEnv)

    elementTool = processingEnv!!.elementUtils
    typeTool = processingEnv.typeUtils
    message = processingEnv.messager
    filer = processingEnv.filer

    message!!.printMessage(Diagnostic.Kind.NOTE,"init 注解处理器初始化")

    val s = processingEnv.options["moduleName"]
    message!!.printMessage(Diagnostic.Kind.NOTE,"--------->$s")
}

通过ProcessingEnvironment获取options,通过key来获取value,将参数从message打印出来

这样,注解处理器的初始化操作就完成,如果app壳中想要使用我们自定义的注解处理器,那么就需要依赖

app壳 # build.gradle

dependencies {

    dependency.each { k, v -> implementation v }

    if (rootProject.isRelease) {
        //依赖包
        implementation project(path: ':register')
        implementation project(path: ':lay_router')
		//kotlin使用kapt依赖 注解处理器,
		//Java使用 annotationProcessor
        kapt project(path: ':lay_compiler')
    }

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

踩坑:创建的注解处理器没有工作

你以为这样就可以完成了吗,错误❎,这里踩了一个坑,导包依赖之后,无论怎么编译也进不到初始化方法中
Android组件化设计2 --- 自定义注解处理器(APT)_第4张图片
当完成注解处理器的初始化操作后,需要在main文件夹下,创建resource — META-INF — services下,创建javax.annotation.processing.Processor文件,其中需要声明我们创建的注解处理器,就相当于注册了
Android组件化设计2 --- 自定义注解处理器(APT)_第5张图片
这里这个坑,真的是踩了一下午…

你可能感兴趣的:(技术,android,gradle,java,apt,注解)