Arouter之注解处理器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Arouter之注解处理器相关的知识,希望对你有一定的参考价值。
参考技术A Arouter是阿里开源的一款android组件化的路由框架。它可以实现一个项目中模块间解耦,实现模块在不依赖其他模块的情况下也可以与其他模块进行通信。Arouter的使用不在此文章做接收,有兴趣到Arouter的github上查看其使用。
Arouter既然是路由框架,那么就少不了路由表,导航、服务等都依赖路由表,而路由表的生成是依赖注解及编译时注解处理器,在编译期间,路由注解器会解析路由注解信息,并按照规则、模板自动生成路由、路由表等java文件。
注解处理器是在编译期解析源码中的注解信息,在根据规则、模板自动生成相应的java文件,那么注解处理器是在编译期的哪个阶段执行呢?
下图是Android Gradle源码编译的大概流程:
Gradle源码编译是会调用javac的编译程序,接着调用程序中的注解器执行,执行注解器任务发生在源码真正编译之前。
Arouter目前提供的注解有以下三种:(其中Param被抛弃,推荐使用Autowried替换)
@Route的作用范围限定在类、接口、枚举及注解,生命周期为CLASS(只保留到编译器class文件中,jvm运行时被丢弃),参数有:
@Autowrited的作用范围是字段,生命周期为CLASS(只保留到编译器class文件中,jvm运行时被丢弃),参数有:
@Interceptor 的作用范围是类、接口、枚举及注解,生命周期为CLASS(只保留到编译器class文件中,jvm运行时被丢弃),参数有:
注解器抽象类是AbstractProcessor,一般自定义注解器都需要继承并实现其抽象方法,主要方法有:
注解器函数执行的流程是:构造方法——》init方法——》process方法
注解处理器中一些主要的类:
Element是一个接口,它只在编译期存在和Type是有区别的,Element表示程序的一个元素,如:package、class、interface、method、成员变量、函数参数、泛型类型等。它可以提供当前元素的类型信息,如:类型名称、字段名称、包等。
Element常见的子类有:
Element常见的函数有:
注解器处理器中编译器环境信息,可以提供文件生成器、类型处理工具类、元素处理工具类、日志打印等
注解处理器文件生成器,Arouter使用文件生成器配合使用squareup的javapoet库生成java源码文件
类型处理工具类
元素处理工具类
日志工具类
Arouter注解处理器有:RouteProcessor、AutowiredProcessor、InterceptorProcessor,它们都继承与BaseProcessor,而BaseProcessor继承于AbstractProcessor。
RouteProcessor是生成一个模块的路由表的注解处理器,模块的路由表包括:路由组表、每个路由组组内路由表。
根据上述对RouteProcessor源码解析,我们可以得出以下结论:
1、RouteProcessor注解处理器处理的是@Route注解
2、RouteProcessor会生成三类java文件,分别是:RouteRoot文件(每个模块一个,模块根路由表文件)、RouteGroup文件(每个模块存在多个分组,每个组对应一个RouteGroup文件)、Provider文件(每个模块一个,模块提供的服务表文件)
3、RouteProcessor是围绕@route的注解,解析生成RouteRoot、RouteGroup、Provider这三种文件,其流程:
3.1、从编译环境RoundEnvironment中读取所有被@Route注解修饰的元素列表
3.2、生成路由表文件RouteRoot的方法loadIntod的参数及名称
3.3、生成路由组文件RouteGroup的方法loadInto的参数及名称
3.4、生成路由组文件Provider的方法loadInto的参数及名称
3.5、遍历@Route注解修饰的元素列表
3.5.1、根据注解信息生成路由元信息对象RouteMeta,同时收集activity、fragment中需要依赖注入@Autowrited的参数类型及配置信息
3.5.2、对RouteMeta进行分组,把RouteMeta放入对应的路由组表groupMap中,组名使用注解中的group,如果为空则使用path中第一段//作为组名
3.6、遍历分好组的路由组groupMap
3.6.1、对provider进行过滤,并生成provider文件的方法及方法参数及方法内容;
3.6.2、同时生成每个路由组对应的RouteGroup文件中的方法及参数及方法内容
3.6.3、生成每个路由组对应的RouteGroup文件
3.6.4、将模块中的路由组RouteGroup进行归档到根路由表routeMap中
3.7、遍历根路由表routeMap生成根路由文件的loadInto方法的方法内容
3.8、根据生成provider的loadInto方法、方法参数、参数名称、方法内容生成provider文件
3.9、根据生成根路由的loadInto方法、方法参数、参数名称、方法内容生成根路由文件RouteRoot
AutowiredProcessor注解处理器是用来生成依赖注入工具类,对被@Autowrited注解修饰的字段进行赋值,我们先分析其源码:
ARouter使用自定义注解处理器,自动生成跳转Activity的代码,避免手动填写和管理path
大家都知道ARouter要跳转目标Activity需要两步:
1.在Activity上添加Route
注解
// Add annotations on pages that support routing (required)
// The path here needs to pay attention to need at least two levels : /xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity
...
2.执行ARouter.getInstance().build("/test/activity").navigation()
方法即可实现跳转
// 1. Simple jump within application (Jump via URL in 'Advanced usage')
ARouter.getInstance().build("/test/activity").navigation();
// 2. Jump with param bundle
ARouter.getInstance().build("/test/1")
.withBundle("key1", bundle)
.navigation();
这就导致了我们需要去管理大量的path字符串,为什么ARouter不能在编译时期帮我们生成一个中间类去管理这些path字符串呢?
既然官方不支持那就自己来做一个吧,整体思路如下:
1.定义两个Activity并添加Route
注解
@Route(path = "/login/LoginActivity")
class LoginActivity : AppCompatActivity()
@Route(path = "/login/RegisterActivity")
class RegisterActivity :BaseActivity()
2.通过自定义注解处理器,处理所有带Route
注解的类,并取出注解里的path
变量值,生成一个中间类用于管理所有的path
,并封装跳转的方法,生成的中间类如下:
package com.agilezhu.processor.generate;
import android.os.Bundle;
import java.lang.String;
public final class ARouterPage
LoginActivity LoginActivity;
RegisterActivity RegisterActivity;
public ARouterPage()
LoginActivity.path = "/login/LoginActivity";
RegisterActivity.path = "/login/RegisterActivity";
public static class LoginActivity
public static String path;
static
path= "/login/LoginActivity";
public static void navigation(String key, Bundle params)
com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).withBundle(key,params).navigation();
public static void navigation()
com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).navigation();
public static class RegisterActivity
public static String path;
static
path= "/login/RegisterActivity";
public static void navigation(String key, Bundle params)
com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).withBundle(key,params).navigation();
public static void navigation()
com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).navigation();
3.使用方法
//不带参数
ARouterPage.LoginActivity.navigation()
//带Bundle类型参数
ARouterPage.RegisterActivity.navigation("params",Bundle())
//也可以使用原有ARouter跳转方式携带各种参数
ARouter.getInstance().build(ARouterPage.LoginActivity.path).withBoolean("key",true).navigation();
有了注解处理器相当于自动帮我们生成了一个管理所有path的类:
- 可以通过类似调用目标Activity的方式进行跳转;
- 简化了ARouter跳转Acticity的代码;
- 每次添加、修改、删除对应的类,都不需要手动修改管理类代码;
注解处理器开发过程
-
添加一个java 类型Module
-
build.gradle
内容如下:plugins id 'java-library' dependencies implementation fileTree(dir: 'libs', include: ['*.jar']) annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'//auto-service本身也是个注解处理器 implementation 'com.google.auto.service:auto-service:1.0-rc7'//注解 processor 类,并对其生成 META-INF 的配置信息 implementation 'com.squareup:javapoet:1.13.0' //通过类调用的形式来生成java代码,避免手动拼接字符串 implementation "com.alibaba:arouter-annotation:1.0.6" java sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7
-
注解处理器核心类:
package com.agilezhu.processor; import com.alibaba.android.arouter.facade.annotation.Route; import com.google.auto.service.AutoService; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import java.util.LinkedHashSet; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.tools.Diagnostic; /** * Generate ARouterPage.java for ARouter */ @AutoService(Processor.class) public class ARouterProcessor extends AbstractProcessor private Filer mFiler; //文件相关工具类:用于保存生成的java类文件 private Elements mElementUtils; //元素相关工具类:用于获取java类文件 private Messager mMessager;//用于打印日志 @Override public synchronized void init(ProcessingEnvironment processingEnvironment) super.init(processingEnvironment); mFiler = processingEnv.getFiler(); mElementUtils = processingEnv.getElementUtils(); mMessager = processingEnv.getMessager(); @Override public Set<String> getSupportedAnnotationTypes() //返回该注解处理器能够处理哪些注解 Set<String> types = new LinkedHashSet<>(); types.add(Route.class.getName()); return types; @Override public SourceVersion getSupportedSourceVersion() //返回当前注解处理器支持的java版本号 return SourceVersion.latest(); @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) if (set == null || set.size() == 0) return false; //将要生成的java完整类名 String targetClassName = "ARouterPage"; String packageName = "com.agilezhu.processor.generate"; //创建方法(构造方法) MethodSpec.Builder bindMethodBuilder = MethodSpec.methodBuilder("<init>") .addModifiers(Modifier.PUBLIC); //创建类 TypeSpec.Builder aRouterPageClassBuilder = TypeSpec.classBuilder(targetClassName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); //获取所有的源码文件 Set<? extends Element> elements = roundEnvironment.getRootElements(); for (Element element : elements) if (!(element instanceof TypeElement)) //判断是否class类 continue; //转换成class类型 TypeElement typeElement = (TypeElement) element; //当前文件的类名 String classSimpleName = element.getSimpleName().toString(); Route routeAnnotation = element.getAnnotation(Route.class); if (routeAnnotation == null) continue; mMessager.printMessage(Diagnostic.Kind.WARNING, "myProcess=====>>>>>" + classSimpleName); ClassName innerClassName = ClassName.get(packageName, targetClassName, classSimpleName); try aRouterPageClassBuilder.addField(innerClassName, classSimpleName) .addType(TypeSpec.classBuilder(innerClassName.simpleName()) .addModifiers(Modifier.STATIC, Modifier.PUBLIC) .addField(String.class, "path", Modifier.PUBLIC, Modifier.STATIC) .addStaticBlock(CodeBlock.builder().addStatement("path= \\"" + routeAnnotation.path() + "\\"").build()) .addMethod(MethodSpec .methodBuilder("navigation") .addParameter(String.class,"key") .addParameter(ClassName.get("android.os","Bundle"), "params") .addStatement("com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).withBundle(key,params).navigation()") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .build()) .addMethod(MethodSpec .methodBuilder("navigation") .addStatement("com.alibaba.android.arouter.launcher.ARouter.getInstance().build(path).navigation()") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .build()) .build()); catch (Exception e) e.printStackTrace(); //构造方法中添加初始化代码 bindMethodBuilder.addStatement(classSimpleName + ".path = \\"" + routeAnnotation.path() + "\\""); aRouterPageClassBuilder.addMethod(bindMethodBuilder.build()); //创建java文件 JavaFile bindProxyFile = JavaFile .builder(packageName, aRouterPageClassBuilder.build()) .build(); try //保存java类文件 bindProxyFile.writeTo(mFiler); catch (Throwable e) e.printStackTrace(); return false;
-
业务模块中引用:
kapt project(path: ':lib:processor') //这里用的kapt处理Kotlin代码
-
重新编译后,在当前模块
build/generated/source/kapt/debug/com/agilezhu/processor/generate/ARouterPage.java
路径下就能看到生成的类
以上是关于Arouter之注解处理器的主要内容,如果未能解决你的问题,请参考以下文章
ARouter使用自定义注解处理器,自动生成跳转Activity的代码,避免手动填写和管理path
ARouter使用自定义注解处理器,自动生成跳转Activity的代码,避免手动填写和管理path