android 路由总线,利用APT实现android路由框架一

随着业务的发展,项目会越来越大,实现组件化便于更好的维护项目以及拆分。

a20658b35477

3.png

业务组件相互独立,需要路由来完成之间的跳转与数据传递。

实现方法:

1、 使用 EventBus的方式,缺点是:EventBean维护成本太高,不好去管理:

2、 使用广播的方式,缺点是:不好管理,都统一发出去了

3、 使用隐士意图方式,缺点是:在AndroidManifest.xml里面配置xml写的太多了

4、 使用类加载方式,缺点就是,容易写错包名类名,缺点较少

5、 使用全局Map的方式,缺点是,要注册很多的对象

6、事件总线的思路。(可以参考CC)

1~5这些不好维护,我们可以利用APT、JavaPoet技术参考阿里的ARouter、ButterKnife等思路来完成自己的一个Router路由框架,先了解一下APT以及JavaPoet。

APT: annotation process tool是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。

APT 编译的时候 ----------> 处理注解 --------> 生成代码----------> 编译完成

JavaPoet: JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件,是Java面向对象OOP语法 ,可以很方便的使用它根据注解生成对应代码,通过这种自动化生成代码的方式, 可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。

传统的APT : package ---> class ---> method

JavaPoet: method---->class----->package

我们看一下我们项目的框架

a20658b35477

image.png

我们类跳转的时候,先找到group,在找到对应的path就可以跳转的目标了。我们可以用两个Map来实现group和path映射。先看一下效果:

group

public class ARouter$$Group$$user implements ARouterGroup {

@Override

public Map> getGroupMap() {

Map> groupMap = new HashMap<>();

groupMap.put("user",ARouter$$Path$$user.class);

return groupMap;

}

}

path

public class ARouter$$Path$$user implements ARouterPath {

@Override

public Map getPathMap() {

Map pathMap = new HashMap<>();

pathMap.put("/user/User_LoginAcitivty", RouterBean.create(RouterBean.TypeEnum.ACTIVITY,User_LoginAcitivty.class,"/user/User_LoginAcitivty","user"));

pathMap.put("/user/User_MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY,User_MainActivity.class,"/user/User_MainActivity","user"));

return pathMap;

}

}

1、我们可以仿照ARouter的架构来搭建自己的框架

a20658b35477

image.png

arouter-annotation、arouter-compiler为java-library。把注解列存放在arouter-annotation包中。(注解不清楚的先了解下)

a20658b35477

image.png

arouter-compiler的gradle要依赖

a20658b35477

image.png

ArouterPath接口

public interface ARouterPath {

/**

* 存放的内容key:/app/MainActivity value:RouterBean == App_MainActivity.class

*/

Map getPathMap();

}

RouterBean用于封装app、user等组件中的MainActivity等对象, 封装成对象会有更多的属性

public class RouterBean {

public enum TypeEnum {//可扩展 fragment

ACTIVITY

}

private TypeEnum typeEnum;//枚举类型activity

private Element element;//节点 JavaPoet,可以拿到很多的信息

private Class> myClass;// 被注解 class对象 eg: MainActivity.class

private String path;// 路由地址 eg: /app/MainActivity

private String group;// 路由组 eg: app user等

public TypeEnum getTypeEnum() {

return typeEnum;

}

public void setTypeEnum(TypeEnum typeEnum) {

this.typeEnum = typeEnum;

}

public Element getElement() {

return element;

}

public void setElement(Element element) {

this.element = element;

}

public Class> getMyClass() {

return myClass;

}

public void setMyClass(Class> myClass) {

this.myClass = myClass;

}

public String getPath() {

return path;

}

public void setPath(String path) {

this.path = path;

}

public String getGroup() {

return group;

}

public void setGroup(String group) {

this.group = group;

}

private RouterBean(TypeEnum typeEnum, Class> myClass, String path, String group) {

this.typeEnum = typeEnum;

// this.element = element;

this.myClass = myClass;

this.path = path;

this.group = group;

}

//对外暴露 为了方便APT生成代码

public static RouterBean create(TypeEnum typeEnum, Class> clazz,String path, String group) {

return new RouterBean(typeEnum, clazz, path, group);

}

/**

* 构建者模式

*/

public static class Builder {

// 枚举类型:Activity

private TypeEnum type;

// 类节点

private Element element;

// 注解使用的类对象

private Class> clazz;

// 路由地址

private String path;

// 路由组

private String group;

public Builder addType(TypeEnum type) {

this.type = type;

return this;

}

public Builder addElement(Element element) {

this.element = element;

return this;

}

public Builder addClazz(Class> clazz) {

this.clazz = clazz;

return this;

}

public Builder addPath(String path) {

this.path = path;

return this;

}

public Builder addGroup(String group) {

this.group = group;

return this;

}

public RouterBean build() {

if (path == null || path.length() == 0) {

throw new IllegalArgumentException("path路径为空,请检查" + clazz);

}

return new RouterBean(this);

}

}

private RouterBean(Builder builder) {

this.typeEnum = builder.type;

this.element = builder.element;

this.myClass = builder.clazz;

this.path = builder.path;

this.group = builder.group;

}

@Override

public String toString() {

return "RouterBean{" +

"path='" + path + '\'' +

", group='" + group + '\'' +

'}';

}

}

public interface ARouterGroup {

/**

*

* 存放的内容key:app value:app所有的path类

*/

Map> getGroupMap();

}

工具类

package com.htf.arouter_compiler.util;

public interface ProcessorConfig {

// @ARouter注解 的 包名 + 类名

String AROUTER_PACKAGE = "com.htf.arouter_annotation.ARouter";

//

String PARAMETER_PACKAGE = "com.htf.arouter_annotation.Parameter";

// 接收module参数

String OPTIONS = "moduleName";

// 目的是接收 包名

String APT_PACKAGE = "packageNameForAPT";

// 为了匹配加注解的类是否为Activity

public static final String ACTIVITY_PACKAGE = "android.app.Activity";

// ARouter api 包名

String AROUTER_API_PACKAGE = "com.htf.arouter_api";

// ARouter api 的 ARouterPath 高层标准

String AROUTER_API_PATH = AROUTER_API_PACKAGE + ".ARouterPath";

// ARouter api 的 ARouterGroup 高层标准

String AROUTER_API_GROUP = AROUTER_API_PACKAGE + ".ARouterGroup";

// 路由组,中的 Path 里面的 方法名

String PATH_METHOD_NAME = "getPathMap";

// 路由组,中的 Path 里面 的 变量名

String PATH_VAR1 = "pathMap";

// 路由组,PATH 最终要生成的 文件名

String PATH_FILE_NAME = "ARouter$$Path$$";

// 路由组,中的 Group 里面的 方法名

String GROUP_METHOD_NAME = "getGroupMap";

// 路由组,中的 Group 里面 的 变量名 1

String GROUP_VAR1 = "groupMap";

// 路由组,GROUP 最终要生成的 文件名

String GROUP_FILE_NAME = "ARouter$$Group$$";

// ARouter api 的 ParameterGet 高层标准

String AROUTER_AIP_PARAMETER_DATA = AROUTER_API_PACKAGE + ".ParameterData";

// ARouter api 的 ParmeterGet 方法的名字

String PARAMETER_METHOD_NAME = "getParameter";

// ARouter api 的 ParameterGet 方法参数的名字

String PARAMETER_NAME = "targetParameter";

// String全类名

String STRING = "java.lang.String";

//用户判断是否是自定义对象类型 要实现Parcelable

String PARCELABLE = "android.os.Parcelable";

// ARouter aip 的 ParmeterGet 的 生成文件名称 $$Parameter

String PARAMETER_FILE_NAME = "$$Parameter";

//用户判断是否是List类型

String LIST_STRING = "java.util.List";

String LIST_PARCELABLE = "java.util.List";

}

利用APT生成ARouter$$Path$$user和ARouter$$Group$$user文件

@AutoService(Processor.class)

@SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedAnnotationTypes("com.htf.arouter_annotation.ARouter")//自己的注解类

@SupportedOptions({"moduleName", "packageNameForAPT"})// moduleName接收module参数;packageNameForAPT目的是接收 包名

public class ARouterProcessor extends AbstractProcessor {

private Types typeTool; type(类信息)的工具类,包含用于操作TypeMirror的工具方法

private Elements elementTool;// 操作Element的工具类(类,函数,属性,其实都是Element)

private Messager messager;

private Filer filer;

private String options; // 各个模块传递过来的模块名 例如:app user zixun order等

private String aptPackage; // 各个模块传递过来的目录 用于统一存放 apt生成的文件

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

// Map

private Map mAllGroupMap = new HashMap<>();

@Override

public synchronized void init(ProcessingEnvironment processingEnvironment) {

super.init(processingEnvironment);

elementTool = processingEnvironment.getElementUtils();

filer = processingEnvironment.getFiler();

messager = processingEnvironment.getMessager();

typeTool = processingEnvironment.getTypeUtils();

options = processingEnvironment.getOptions().get(ProcessorConfig.OPTIONS);

aptPackage = processingEnvironment.getOptions().get(ProcessorConfig.APT_PACKAGE);

}

@Override

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

if (set.isEmpty()) {

messager.printMessage(Diagnostic.Kind.NOTE, "没有发现注解的类");

return false;

}

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

Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);

// 通过Element工具类,获取android.app.Activity类型

TypeElement activityElement = elementTool.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);

TypeMirror activityMirror = activityElement.asType();

for (Element element : elements) {

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

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

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

RouterBean routerBean = new RouterBean.Builder()

.addElement(element)

.addGroup(options)

.addPath(aRouter.path())

.build();

TypeMirror myMirror = element.asType();//自己类的TypeMirror 用于判断是不是继承android.app.Activity

if (typeTool.isSubtype(myMirror, activityMirror)) {//是activity

routerBean.setTypeEnum(RouterBean.TypeEnum.ACTIVITY);

} else {

throw new RuntimeException(className + "必须是Activity");

}

//把所有的注解类封以module名为key封装到mAllPathMap中

if (checkRouterPath(routerBean)) {

List routerBeanList = mAllPathMap.get(routerBean.getGroup());

if (ProcessorUtils.isEmpty(routerBeanList)) {

routerBeanList = new ArrayList<>();

routerBeanList.add(routerBean);

mAllPathMap.put(routerBean.getGroup(), routerBeanList);

} else {

routerBeanList.add(routerBean);

}

} else {

messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");

}

}//循环结束,把所有的注解类以module名为key封装到mAllPathMap

// TODO 开始用javapoet生成文件

//定义文件要实现的接口

TypeElement pathTypeElement = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_PATH);

try {

createPathFile(pathTypeElement);

} catch (IOException e) {

e.printStackTrace();

}

TypeElement groupType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_GROUP);

try {

createGroupFile(groupType, pathTypeElement);

} catch (IOException e) {

e.printStackTrace();

}

return true;

}

//生成路由组Group文件,如:ARouter$$Group$$app

private void createGroupFile(TypeElement groupType, TypeElement pathType) throws IOException {

if (ProcessorUtils.isEmpty(mAllGroupMap) || ProcessorUtils.isEmpty(mAllPathMap)) return;

//要生成的内容

//Map>

TypeName methodReturn = ParameterizedTypeName.get(

ClassName.get(Map.class),

ClassName.get(String.class),

//Class extends ARouterPath>>

ParameterizedTypeName.get(ClassName.get(Class.class),

WildcardTypeName.subtypeOf(ClassName.get(pathType))) // ? extends ARouterLoadPath)

);

MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.GROUP_METHOD_NAME)

.addAnnotation(Override.class)

.addModifiers(Modifier.PUBLIC)

.returns(methodReturn);

//方法里的内容

// Map> groupMap = new HashMap<>();

methodBuilder.addStatement("$T $N = new $T<>()",

ClassName.get(Map.class),

ClassName.get(String.class),

//Class extends ARouterPath>

ParameterizedTypeName.get(ClassName.get(Class.class),

WildcardTypeName.subtypeOf(ClassName.get(pathType))), // ? extends ARouterPath

ProcessorConfig.GROUP_VAR1,

ClassName.get(HashMap.class));

// groupMap.put("app", ARouter$$Path$$app.class);

// groupMap.put("user", ARouter$$Path$$user.class);

methodBuilder.addStatement("$N.put($S,$T.class)",

ProcessorConfig.GROUP_VAR1,

options,

ClassName.get(aptPackage, mAllGroupMap.get(options)));

methodBuilder.addStatement("return $N", ProcessorConfig.GROUP_VAR1);

///生成文件

String fileClassName = ProcessorConfig.GROUP_FILE_NAME + options;

JavaFile.builder(aptPackage,

TypeSpec.classBuilder(fileClassName)

.addSuperinterface(ClassName.get(groupType))

.addModifiers(Modifier.PUBLIC)

.addMethod(methodBuilder.build())

.build())

.build()

.writeTo(filer);

}

private void createPathFile(TypeElement typeElement) throws IOException {

//先把方法返回类型生成 Map

ParameterizedTypeName methodReturn = ParameterizedTypeName.get(

ClassName.get(Map.class),

ClassName.get(String.class),

ClassName.get(RouterBean.class)

);

//生成方法

MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PATH_METHOD_NAME)

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.returns(methodReturn);

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

方法里的内容 Map pathMap = new HashMap<>();

methodBuilder.addStatement("$T $N = new $T<>()",

ClassName.get(Map.class),

ClassName.get(String.class),

ClassName.get(RouterBean.class),

ProcessorConfig.PATH_VAR1,

ClassName.get(HashMap.class));

List routerBeanList = entry.getValue();

for (RouterBean routerBean : routerBeanList) {

methodBuilder.addStatement("$N.put($S, $T.create($T.$L, $T.class, $S, $S))",

ProcessorConfig.PATH_VAR1,

routerBean.getPath(),

ClassName.get(RouterBean.class),

ClassName.get(RouterBean.TypeEnum.class),

routerBean.getTypeEnum(),

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

routerBean.getPath(),

routerBean.getGroup());

}

// return pathMap;

methodBuilder.addStatement("return $N", ProcessorConfig.PATH_VAR1);

}

//生成的类文件名ARouter$$Path$$user 有implements所以方法和类要合为一体生成

String fileName = ProcessorConfig.PATH_FILE_NAME + options;

messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由Path类文件:" +

aptPackage + "." + fileName);

JavaFile.builder(aptPackage,

TypeSpec.classBuilder(fileName)

.addSuperinterface(ClassName.get(typeElement))

.addMethod(methodBuilder.build())

.addModifiers(Modifier.PUBLIC)

.build())

.build()

.writeTo(filer);

mAllGroupMap.put(options, fileName);

}

private boolean checkRouterPath(RouterBean bean) {

String group = bean.getGroup();//app、user、zixun等module

String path = bean.getPath();// /app/MainActivity

// TODO 校验path

// @ARouter注解中的path值,必须要以 / 开头

if (ProcessorUtils.isEmpty(path) || !path.startsWith("/")) {

messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的path值,必须要以 / 开头");

return false;

}

if (path.lastIndexOf("/") == 0) {//开发者必须遵循

messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");

return false;

}

// 从第一个 / 到第二个 / 中间截取,如:/app/MainActivity 截取出 app,order,personal 作为group

String finalGroup = path.substring(1, path.indexOf("/", 1));

if (!ProcessorUtils.isEmpty(group) && !group.equals(options)) {

messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的group值必须和子模块名一致!");

return false;

} else {

bean.setGroup(finalGroup);

}

return true;

}

}

ARouterProcessor会检测带注解,利用roundEnvironment.getElementsAnnotatedWith(ARouter.class);来判断哪些是自己ARouter注解类,然后javapoet语法生成Java文件。build一下项目。

不要忘了在类上添加注解

a20658b35477

image.png

最后生成文件的效果

a20658b35477

image.png

最后调用

ARouter$$Group$$user group$$user = new ARouter$$Group$$user();

Map> groupMap = group$$user .getGroupMap();

Class extends ARouterPath> myClass = groupMap.get("user ");

try {

ARouter$$Path$$user path = (ARouter$$Path$$user ) myClass.newInstance();

Map pathMap = path.getPathMap();

RouterBean bean = pathMap.get("/user /User_LoginAcitivty");

if (bean != null) {

Intent intent = new Intent(this, bean.getMyClass());

startActivity(intent);

}

} catch (Exception e) {

e.printStackTrace();

}

现在调用还复杂化、不能参数据,在第二篇文章将会再次封装,源码链接放在第二篇文章里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值