java路由架构_组件化的架构设计(二):路由架构设计及编码实现

组件化路由架构设计思路

上一篇中讲解了类加载、全局Map记录实现组件化模块之间的交互,慢慢衍生APT技术。

那么在组件化架构中,我们需要思考通过APT+javapoet技术生成什么样的类文件呢?

java路由架构_组件化的架构设计(二):路由架构设计及编码实现_第1张图片

从组件化路由架构设计图需要思考,使用APT生成文件为什么需要分组? 生成这些文件干什么用?

设计思路:在初始化只加载分组表数据,在使用的时候,如果使用A分组就去加载A分组下的所有路由信息,而不去加载B分组数据。比如:A组有100个路由信息,B组有200个路由信息,如果不分组,Map中就需要加载300个路由信息,当用户可能根本就不需要进入B分组页面,加载B分组的路由信息,会导致内存和时间的浪费。

java路由架构_组件化的架构设计(二):路由架构设计及编码实现_第2张图片

todo_modular项目:使用路由组件化项目

app: 主工程(application)

common:基础模块(library)

shop:购物shop功能模块

personal:我的personal功能模块

路由框架

annotations(java library):注解模块,自定义注解给api和compiler依赖

compiler:APT模块(java library)编译时生成java文件

api:核心模块,项目中需要引入依赖的模块,使用compiler模块生成的java类完成路由表的创建

设计Coding实现

RouteMeta 路由信息封装类,路由地址,路由分组名,原始的类元素等。

import javax.lang.model.element.Element;

public class RouteMeta {

public enum RouteType {

ACTIVITY

}

// 路由类型,支持Activity

private RouteType type;

// 原始的类元素

private Element element;

// 注解使用的类对象

private Class> clazz;

// 路由地址

private String path;

// 路由分组名

private String group;

}

思考:为什么Element类节点需要存起来?

因为要在注解处理器中,循环拿到每个类节点,方便赋值和调用。Element它是javax.lang包下的,不属于Android Library。

IRouteRoot接口

Map>

key: 组名,如:app , value:该组名下所有的路径文件名,如:ARouter$$Group$$app.class

注意:需要生成路由路径文件后ARouter$$Group$$app.class,才能生成路由组文件,使用接口方式容易扩展

/**

* 路由组加载数据接口

*/

public interface IRouteRoot {

/**

* 加载路由组数据

* 如:key:"app", value: ARouter$$Group$$app.class(实现了IRouteGroup接口)

*/

void loadInto(Map> routes);

}

IRouteGroup接口

Map

key:路径名,如:"/app/MainActivity", value:该路径名下对应的路由信息

OOP思想,让单纯的targetClass变成更灵活的RouteMeta对象,接口方式容易扩展

/**

* 路由组对应的详细Path路径加载数据接口

* 如:app组下对应有哪些类需要加载

*/

public interface IRouteGroup {

/**

* 加载路由组中的Path详细数据

* 如:key:"/app/MainActivity", value:MainActivity信息封装到RouteMeta对象中

*/

void loadInto(Map atlas);

}

ARouter$$Group$$XX 与 ARouter$$Root$$XX 文件生成

下面以购物模块shop为例,生成ARouter$$Group$$shop和 ARouter$$Root$$shop文件,涉及到的技术点:APT + javapoet

在使用APT + javapoet 生成我们想要的代码之前,首先要设计好我们想要的代码模版,如:ARouter$$Root$$shop 分组的模版代码

public final class ARouter$$Root$$shop implements IRouteRoot {

@Override

public void loadInto(final Map> routes) {

routes.put("shop", ARouter$$Group$$shop.class);;

}

}

ARouter$$Group$$shop 该分组下的所有的路由信息代码模版

public class ARouter$$Group$$shop implements IRouteGroup {

@Override

public void loadInto(Map atlas) {

atlas.put("/shop/ShopActivity", RouteMeta.build(RouteMeta.RouteType.ACTIVITY, ShopActivity.class, "/shop/ShopActivity", "shop"));;

atlas.put("/shop/ShopDetailActivity", RouteMeta.build(RouteMeta.RouteType.ACTIVITY, ShopDetailActivity.class, "/shop/ShopDetailActivity", "shop"));;

}

}

为了生成上面的模版代码,首先第一个@Router注解。实现Activity跳转,就要用到该注解标记Activity,通过注解处理器处理这些注解,然后在编译器生成代码:

@Target(ElementType.TYPE) // 该注解作用在类之上

@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作。注解会在class文件中存在

public @interface Router {

// 详细路由路径(必填),如:"app/MainActivity"

String path();

// 路由组名(选填,如果不填写,可以从path中截取)

String group() default "";

}

然后创建路由注解处理器,类名为ARouterProcessor,该类继承AbstractProcessor。注解处理器需要注册,借助AutoService可以帮助我们自动注册到META-INF中。同时还需要指定注解处理器支持的注解类型、JDK编译的版本、注解处理器接收的参数等。

// 允许/支持的注解类型,让注解处理器处理(新增annotation module)

@SupportedAnnotationTypes({"com.example.modular.annotations.Router"})

// 指定JDK编译版本

@SupportedSourceVersion(SourceVersion.RELEASE_7)

// 注解处理器接收的参数

@SupportedOptions({"moduleName"})

// AutoService则是固定的写法,加个注解即可

// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册

// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件

@AutoService(Processor.class)

public class ARouterProcessor extends AbstractProcessor { }

接下来实现process抽象方法,该方法相当于main方法,注解处理器的入口方法,用来处理被@Router标记的元素。

/**

* 相当于main函数,开始处理注解

* 注解处理器的核心方法,处理具体的注解,生成Java文件

*

* @param set 支持注释类型的集合,如:@ARouter注解

* @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解

* @return true 表示后续处理器不会再处理(已经处理完成)

*/

@Override

public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {

// set集合,就是支持的注解集合,如:ARouter注解

if (set.isEmpty()) return false;

// 获取所有被@ARouter注解注释的元素

Set extends Element> elementsAnnotatedWithARouter = roundEnvironment.getElementsAnnotatedWith(Router.class);

if (elementsAnnotatedWithARouter != null && !elementsAnnotatedWithARouter.isEmpty()) {

try {

parseElements(elementsAnnotatedWithARouter);

} catch (IOException e) {

messager.printMessage(Diagnostic.Kind.NOTE, "异常发生:" + e.getMessage());

}

}

return true;

}

在处理被@Router注解标记的元素之前,需要初始化一些工具,重写AbstractProcessor类的init方法,通过processingEnvironment获取辅助工具,如:打印Log的工具、 文件生成工具等。

// 操作Element的工具类(如:类、函数、属性都是Element)

private Elements elementUtils;

// Messager用来报告错误,警告和其他提示信息

private Messager messager;

// type(类信息)工具类,包含用于操作TypeMirror的工具方法

private Types typeUtils;

// 文件生成器,类/资源,Filter用来创建新的源文件,class文件以及辅助文件

private Filer filer;

// 模块名,通过getOptions获取build.gradle传过来

private String moduleName;

// 缓存路由信息,key:组名 value:该组名下的所有路由信息

private Map> cacheRouteMetaMap = new HashMap<>();

// 缓存分组的信息,key:组名 value:该组名下对应的路由类名,如:Router$$Group$$shop

private Map cacheGroupMap = new HashMap<>();

/**

* 该方法主要用于一些初始化工作,通过该方法的processingEnvironment参数可以获取一些工具

*/

@Override

public synchronized void init(ProcessingEnvironment processingEnvironment) {

super.init(processingEnvironment);

elementUtils = processingEnvironment.getElementUtils();

messager = processingEnvironment.getMessager();

typeUtils = processingEnvironment.getTypeUtils();

filer = processingEnvironment.getFiler();

messager.printMessage(Diagnostic.Kind.NOTE, "<<<<< ARouterProcessor::init >>>>>");

Map options = processingEnvironment.getOptions();

if (options != null && !options.isEmpty()) {

moduleName = options.get("moduleName");

messager.printMessage(Diagnostic.Kind.NOTE, "moduleName=" + moduleName);

}

if (Utils.isEmpty(moduleName)) {

throw new RuntimeException("ARouter注解处理器传入moduleName参数不能为空,清查看build.gradle配置文件配置是否正确");

}

}

解析所有被@Router注解标记的元素,将满足条件元素中路由信息封装到RouteMeta并缓存起来。用于生成分组文件和该组下的路由文件。

private void parseElements(Set extends Element> elements) throws IOException {

TypeElement activityElement = elementUtils.getTypeElement("android.app.Activity");

TypeMirror activityMirror = activityElement.asType();

for (Element element : elements) {

TypeMirror elementMirror = element.asType();

messager.printMessage(Diagnostic.Kind.NOTE, "遍历元素信息:" + elementMirror.toString());

// 遍历元素信息:com.example.modular.shop.ShopActivity

// 通过类节点获取包节点,(全路径名,如:com.example.modular.shop)

String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();

// 获取被@ARouter注解的简单类名

String simpleName = element.getSimpleName().toString();

// 注: 包名:com.example.modular.shop 被注解的类名:ShopActivity

messager.printMessage(Diagnostic.Kind.NOTE, "包名:" + packageName + " 被注解的类名:" + simpleName);

Router aRouter = element.getAnnotation(Router.class);

RouteMeta routeMeta = new RouteMeta(element, aRouter.group(), aRouter.path());

// 判断第一类型是否是第二类型的子类型,如果是返回true

if (typeUtils.isSubtype(elementMirror, activityMirror)) {

// 满足条件:被@ARouter注解的元素是Activity的子类型

routeMeta.setType(RouteMeta.RouteType.ACTIVITY);

} else {

throw new RuntimeException("@ARouter注解目前只能应用与Activity类上");

}

fillMapWithRouteMeta(routeMeta);

}

createGroupFile();

createRootFile();

}

fillMapWithRouteMeta方法用于缓存路由信息

// 缓存分组的信息,key:组名 value:该组名下对应的路由类名,如:Router$$Group$$shop

private Map cacheGroupMap = new HashMap<>();

private void fillMapWithRouteMeta(RouteMeta routeMeta) {

if (checkRouterPath(routeMeta)) {

messager.printMessage(Diagnostic.Kind.NOTE, "routeMeta>>>> " + routeMeta.toString());

List routeMetas = cacheRouteMetaMap.get(routeMeta.getGroup());

if (routeMetas == null) {

routeMetas = new ArrayList<>();

cacheRouteMetaMap.put(routeMeta.getGroup(), routeMetas);

}

routeMetas.add(routeMeta);

} else {

messager.printMessage(Diagnostic.Kind.NOTE, "routeMeta检查失败,不满足规范");

}

}

先创建路由文件,createGroupFile具体实现如下:

private void createGroupFile() throws IOException {

if (cacheRouteMetaMap.isEmpty()) return;

TypeElement routeGroupElement = elementUtils.getTypeElement("com.example.modular.api.IRouteGroup");

for (Map.Entry> entry : cacheRouteMetaMap.entrySet()) {

ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(

ClassName.get(Map.class),

ClassName.get(String.class),

ClassName.get(RouteMeta.class)

);

// Map atlas

ParameterSpec atlasParameter = ParameterSpec.builder(parameterizedTypeName, "atlas").build();

// public void loadInto(Map atlas)

MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto")

.addAnnotation(Override.class)

.addParameter(atlasParameter)

.addModifiers(Modifier.PUBLIC);

List routeMetas = entry.getValue();

for (RouteMeta routeMeta : routeMetas) {

// atlas.put("/shop/ShopActivity", RouteMeta.build(RouteMeta.RouteType.ACTIVITY, ShopActivity.class, "/shop/ShopActivity", "shop"));

loadIntoMethodBuilder.addStatement(

"$N.put($S, $T.build($T.$L, $T.class, $S, $S));",

"atlas",

routeMeta.getPath(),

ClassName.get(RouteMeta.class),

ClassName.get(RouteMeta.RouteType.class),

routeMeta.getType(),

ClassName.get((TypeElement) routeMeta.getElement()),

routeMeta.getPath(),

routeMeta.getGroup()

);

}

MethodSpec loadIntoMethod = loadIntoMethodBuilder.build();

String groupName = entry.getKey();

String packageName = "com.example.android.arouter.routers";

String finalClassName = "ARouter$$Group$$" + groupName;

messager.printMessage(Diagnostic.Kind.NOTE, "最终生成的路径的文件名:" + packageName + "." + finalClassName);

// public class Router$$Group$$shop implements IRouteGroup

TypeSpec finalClass = TypeSpec.classBuilder(finalClassName)

.addMethod(loadIntoMethod)

.addModifiers(Modifier.PUBLIC)

.addSuperinterface(ClassName.get(routeGroupElement))

.build();

// 利用文件生成器生成文件

JavaFile.builder(packageName, finalClass).build().writeTo(filer);

cacheGroupMap.put(groupName, finalClassName);

}

}

再创建分组文件,createRootFile方法具体实现如下:

private void createRootFile() throws IOException {

if (cacheGroupMap.isEmpty()) return;

TypeElement routeRootElement = elementUtils.getTypeElement("com.example.modular.api.IRouteRoot");

TypeElement routeGroupElement = elementUtils.getTypeElement("com.example.modular.api.IRouteGroup");

// Map>

ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(

ClassName.get(Map.class),

ClassName.get(String.class),

ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(ClassName.get(routeGroupElement)))

);

// Map> routes

ParameterSpec routesParameter = ParameterSpec.builder(parameterizedTypeName, "routes", Modifier.FINAL).build();

// public void loadInto(Map> routes)

MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto")

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addParameter(routesParameter);

String packageName = "com.example.android.arouter.routers";

for (Map.Entry entry : cacheGroupMap.entrySet()) {

// 如:shop

String groupName = entry.getKey();

// 如:Router$$Group$$shop

String finalGroupClassName = entry.getValue();

// routes.put("shop", Router$$Group$$shop.class);

loadIntoMethodBuilder.addStatement(

"$N.put($S, $T.class);",

"routes",

groupName,

ClassName.get(packageName, finalGroupClassName)

);

}

MethodSpec loadIntoMethod = loadIntoMethodBuilder.build();

String finalClassName = "ARouter$$Root$$" + moduleName;

messager.printMessage(Diagnostic.Kind.NOTE, "最终生成的组文件名:" + packageName + "." +finalClassName);

// public final class Router$$Root$$shop implements IRouteRoot

TypeSpec finalClass = TypeSpec.classBuilder(finalClassName)

.addMethod(loadIntoMethod)

.addModifiers(Modifier.PUBLIC, Modifier.FINAL)

.addSuperinterface(ClassName.get(routeRootElement)).build();

JavaFile.builder(packageName, finalClass).build().writeTo(filer);

}

XX$$ARouter$$Parameter

比如Activity跳转,需要携带了参数,我们需要通过Activity.getIntent()获取参数。这些模版代码也可以注解处理器帮我们生成。

public final class ShopActivity$$ARouter$$Parameter implements IParameter {

@Override

public void inject(final Object object) {

ShopActivity target = (ShopActivity) object;;

target.name = target.getIntent().getStringExtra("name");;

target.age = target.getIntent().getIntExtra("shopAge", target.age);;

target.isOpen = target.getIntent().getBooleanExtra("isOpen", target.isOpen);;

}

}

创建ParameterProcessor注解处理器,用来生成获取参数的模板代码,该类同样继承AbstractProcessor

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.CLASS)

public @interface Parameter {

String name() default "";

}

// 允许/支持的注解类型,让注解处理器处理(新增annotation module)

@SupportedAnnotationTypes({"com.example.modular.annotations.Parameter"})

// 指定JDK编译版本

@SupportedSourceVersion(SourceVersion.RELEASE_7)

// 注解处理器接收的参数

@SupportedOptions({"moduleName"})

// AutoService则是固定的写法,加个注解即可

// 通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册

// 用来生成 META-INF/services/javax.annotation.processing.Processor 文件

@AutoService(Processor.class)

public class ParameterProcessor extends AbstractProcessor {}

实现process方法,处理被@Parameter标记的元素。

@Override

public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {

if (set.isEmpty()) return false;

Set extends Element> elementsAnnotatedWithParameter = roundEnvironment.getElementsAnnotatedWith(Parameter.class);

if (elementsAnnotatedWithParameter != null && !elementsAnnotatedWithParameter.isEmpty()) {

fillMapWithElement(elementsAnnotatedWithParameter);

try {

createParameterFile();

} catch (IOException e) {

messager.printMessage(Diagnostic.Kind.NOTE,

"ParameterProcessor发生异常:" + e.getMessage());

}

return true;

}

return false;

}

其中fillMapWithElement用于缓存所有满足条件的元素

private final Map> cacheMap = new HashMap<>();

private void fillMapWithElement(Set extends Element> elementsAnnotatedWithParameter) {

for (Element element : elementsAnnotatedWithParameter) {

// @Parameter注解作用在字段上,获取字段的父元素

TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

messager.printMessage(Diagnostic.Kind.NOTE,

"@Parameter遍历父元素信息:" + enclosingElement.getSimpleName() + " && 遍历元素信息: " + element.getSimpleName());

List elements = cacheMap.get(enclosingElement);

if (elements == null) {

List fields = new ArrayList<>();

fields.add(element);

cacheMap.put(enclosingElement, fields);

} else {

elements.add(element);

}

}

}

接下来用于创建生成文件

private void createParameterFile() throws IOException {

if (cacheMap.isEmpty()) return;

TypeElement activityElement = elementUtils.getTypeElement("android.app.Activity");

ParameterSpec objectParameter = ParameterSpec.builder(TypeName.OBJECT, "object", Modifier.FINAL).build();

TypeElement parameterType = elementUtils.getTypeElement("com.example.modular.api.IParameter");

for (Map.Entry> entry : cacheMap.entrySet()) {

TypeElement typeElement = entry.getKey();

if (!typeUtils.isSubtype(typeElement.asType(), activityElement.asType())) {

throw new RuntimeException("@Parameter注解目前只能应用于Activity上");

}

ClassName className = ClassName.get(typeElement);

// public void inject(Object object)

MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("inject")

.addAnnotation(Override.class)

.addModifiers(Modifier.PUBLIC)

.addParameter(objectParameter);

// MainActivity target = (MainActivity) object;

injectMethodBuilder.addStatement(

"$T target = ($T) $N;",

className,

className,

"object"

);

List elements = entry.getValue();

for (Element element : elements) {

TypeMirror typeMirror = element.asType();

int type = typeMirror.getKind().ordinal();

String fieldName = element.getSimpleName().toString();

// 获取注解中定义的字段名

String name = element.getAnnotation(Parameter.class).name();

// 如果注解中没有定义,则使用默认的字段名

String finalName = Utils.isEmpty(name) ? fieldName : name;

String finalValue = "target." + fieldName;

String format = finalValue + " = target.getIntent().";

if (type == TypeKind.INT.ordinal()) {

// target.age = target.getIntent().getIntExtra("appAge", target.age);

format += "getIntExtra($S, " + finalValue + ");";

} else if (type == TypeKind.BOOLEAN.ordinal()) {

// target.isOpen = target.getIntent().getBooleanExtra("isOpen", target.isOpen);

format += "getBooleanExtra($S, " + finalValue + ");";

} else {

if (typeMirror.toString().equals("java.lang.String")) {

// target.name = target.getIntent().getStringExtra("name");

format += "getStringExtra($S);";

}

}

if (format.endsWith(";")) {

// target.name = target.getIntent().getStringExtra("name");

injectMethodBuilder.addStatement(format, finalName);

} else {

messager.printMessage(Diagnostic.Kind.ERROR, "目前仅仅支持String int Boolean类型参数");

}

}

MethodSpec injectMethod = injectMethodBuilder.build();

String finalClassName = typeElement.getSimpleName() + "$$ARouter$$Parameter";

messager.printMessage(Diagnostic.Kind.NOTE,

"最终生产的参数类文件:" + className.packageName() + "." + finalClassName);

// public final class MainActivity$$Router$$Parameter implements IParameter

TypeSpec finalClass = TypeSpec.classBuilder(finalClassName)

.addMethod(injectMethod)

.addModifiers(Modifier.PUBLIC, Modifier.FINAL)

.addSuperinterface(ClassName.get(parameterType))

.build();

JavaFile.builder(className.packageName(), finalClass).build().writeTo(filer);

}

}

如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)

你可能感兴趣的:(java路由架构)