架构师(六)——组件化路由架构设计

结构

为什么要分组?如果一个应用里面的activity特别多的话,全部加载进来会非常浪费内存。所以要分组,当你只需要一个模块的时候,只要加载该模块下的activity就行了。

先在项目中创建一个java Library,命名为arouter_annotation
创建注解文件ARouter.java

@Target(ElementType.TYPE) // 该注解作用在类之上
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface ARouter {

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

    // 路由组名(选填,如果开发者不填写,可以从path中截取出来)
    String group() default "";
}

创建一个RouterBean类用来存储class的信息

package com.netease.arouter.annotation.model;

import javax.lang.model.element.Element;

/**
 * 路由路径Path的最终实体封装类
 * 比如:app分组中的MainActivity对象,这个对象有更多的属性
 */
public class RouterBean {

    public enum Type {
        ACTIVITY
    }

    // 枚举类型:Activity
    private Type type;
    // 类节点
    private Element element;
    // 注解使用的类对象
    private Class clazz;
    // 路由地址
    private String path;
    // 路由组
    private String group;

    private RouterBean(Builder builder) {
        this.type = builder.type;
        this.element = builder.element;
        this.clazz = builder.clazz;
        this.path = builder.path;
        this.group = builder.group;
    }

    private RouterBean(Type type, Class clazz, String path, String group) {
        this.type = type;
        this.clazz = clazz;
        this.path = path;
        this.group = group;
    }

    // 对外提供简易版构造方法,主要是为了方便APT生成代码
    public static RouterBean create(Type type, Class clazz, String path, String group) {
        return new RouterBean(type, clazz, path, group);
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public Element getElement() {
        return element;
    }

    public void setElement(Element element) {
        this.element = element;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    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;
    }

    /**
     * 构建者模式
     */
    public static class Builder {

        // 枚举类型:Activity
        private Type type;
        // 类节点
        private Element element;
        // 注解使用的类对象
        private Class clazz;
        // 路由地址
        private String path;
        // 路由组
        private String group;

        public Builder setType(Type type) {
            this.type = type;
            return this;
        }

        public Builder setElement(Element element) {
            this.element = element;
            return this;
        }

        public Builder setClazz(Class clazz) {
            this.clazz = clazz;
            return this;
        }

        public Builder setPath(String path) {
            this.path = path;
            return this;
        }

        public Builder setGroup(String group) {
            this.group = group;
            return this;
        }

        // 最后的build或者create,往往是做参数的校验或者初始化赋值工作
        public RouterBean build() {
            if (path == null || path.length() == 0) {
                throw new IllegalArgumentException("path必填项为空,如:/app/MainActivity");
            }
            return new RouterBean(this);
        }
    }

    @Override
    public String toString() {
        return "RouterBean{" +
                "path='" + path + '\'' +
                ", group='" + group + '\'' +
                '}';
    }
}

新建一个java Library存放接口 arouter_api
创建详细Path加载数据接口

/**
 * 路由组Group对应的详细Path加载数据接口
 * 比如:app分组对应有哪些类需要加载
 */
public interface ARouterLoadPath {

    /**
     * 加载路由组Group中的Path详细数据
     * 比如:"app"分组下有这些信息:
     *
     * @return key:"/app/MainActivity", value:MainActivity信息封装到RouterBean对象中
     */
    Map loadPath();
}

创建路由组Group加载数据接口

/**
 * 路由组Group加载数据接口
 */
public interface ARouterLoadGroup {

    /**
     * 加载路由组Group数据
     * 比如:"app", ARouter$$Path$$app.class(实现了ARouterLoadPath接口)
     *
     * @return key:"app", value:"app"分组对应的路由详细对象类
     */
    Map> loadGroup();
}

编写模拟文件,要用到上述的接口,需要添加依赖,由于所有的子模块都添加了common依赖,所以只要在common中添加arouter_api的双向依赖即可

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation support.appcompat

    // 每个功能子模块既然都要生成APT源文件,而且又是所有模块依赖的公共库
    // 那么公共基础库依赖路由arouter_api就能向每个子模块提供开放api了
    api project(':arouter_api') // 路由对外开放api模块
}

模拟ARouter路由器的组文件,对应的路径文件
ARouter$$Path$$order.java

public class ARouter$$Path$$order implements ARouterLoadPath {

    @Override
    public Map loadPath() {
        Map pathMap = new HashMap<>();
        pathMap.put("/order/Order_MainActivity",
                RouterBean.create(RouterBean.Type.ACTIVITY, Order_MainActivity.class,
                        "/order/Order_MainActivity", "order"));
        return pathMap;
    }
}

模拟ARouter路由器的组文件,对应的路径文件
ARouter$$Group$$order.java

public class ARouter$$Group$$order implements ARouterLoadGroup {

    @Override
    public Map> loadGroup() {
        Map> groupMap = new HashMap<>();
        groupMap.put("order", ARouter$$Path$$order.class);
        return groupMap;
    }
}

当app想要跳转到order模块中的某个activity

public void jumpOrder(View view) {

    // 最终集成化模式,所有子模块app/order/personal通过APT生成的类文件都会打包到apk里面,不用担心找不到
    ARouterLoadGroup group = new ARouter$$Group$$order();
    Map> map = group.loadGroup();
    // 通过order组名获取对应路由路径对象
    Class clazz = map.get("order");

    try {
        // 类加载动态加载路由路径对象
        ARouter$$Path$$order path = (ARouter$$Path$$order) clazz.newInstance();
        Map pathMap = path.loadPath();
        // 获取目标对象封装
        RouterBean bean = pathMap.get("/order/Order_MainActivity");

        if (bean != null) {
            startActivity(new Intent(this, bean.getClazz()));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

接下来写注解处理器
通过build.gradle传入参数

// 在gradle文件中配置选项参数值(用于APT传参接收)
// 切记:必须写在defaultConfig节点下
javaCompileOptions {
    annotationProcessorOptions {
        //moduleName:模块名(app、order、personal)   packageNameForAPT:apt生成在哪个包下
        arguments = [moduleName: project.getName(), packageNameForAPT: packageNameForAPT]
    }
}

其中packageNameForAPT定义在config.gradle中

// 包名,用于存放APT生成的类文件
packageNameForAPT = "com.netease.modular.apt"

常量类

public class Constants {

    // 注解处理器中支持的注解类型
    public static final String AROUTER_ANNOTATION_TYPES = "com.netease.arouter.annotation.ARouter";

    // 每个子模块的模块名
    public static final String MODULE_NAME = "moduleName";
    // 包名,用于存放APT生成的类文件
    public static final String APT_PACKAGE = "packageNameForAPT";


    // Activity全类名
    public static final String ACTIVITY = "android.app.Activity";
    // 包名前缀封装
    static final String BASE_PACKAGE = "com.netease.arouter.api";
    // 路由组Group加载接口
    public static final String AROUTE_GROUP = BASE_PACKAGE + ".core.ARouterLoadGroup";
    // 路由组Group对应的详细Path加载接口
    public static final String AROUTE_PATH = BASE_PACKAGE + ".core.ARouterLoadPath";


    // 路由组Group,参数名
    public static final String GROUP_PARAMETER_NAME = "groupMap";
    // 路由组Group,方法名
    public static final String GROUP_METHOD_NAME = "loadGroup";
    // 路由组Group对应的详细Path,参数名
    public static final String PATH_PARAMETER_NAME = "pathMap";
    // 路由组Group对应的详细Path,方法名
    public static final String PATH_METHOD_NAME = "loadPath";


    // APT生成的路由组Group源文件名
    public static final String GROUP_FILE_NAME = "ARouter$$Group$$";
    // APT生成的路由组Group对应的详细Path源文件名
    public static final String PATH_FILE_NAME = "ARouter$$Path$$";
}

ARouterProcessor.java编写
先获取传入的参数

// 注解处理器接收的参数
@SupportedOptions({Constants.MODULE_NAME, Constants.APT_PACKAGE})
public class ARouterProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        ......
        // 通过ProcessingEnvironment去获取对应的参数
        Map options = processingEnvironment.getOptions();
        if (!EmptyUtils.isEmpty(options)) {
            moduleName = options.get(Constants.MODULE_NAME);
            packageNameForAPT = options.get(Constants.APT_PACKAGE);
            // 有坑:Diagnostic.Kind.ERROR,异常会自动结束,不像安卓中Log.e
            messager.printMessage(Diagnostic.Kind.NOTE, "moduleName >>> " + moduleName);
            messager.printMessage(Diagnostic.Kind.NOTE, "packageNameForAPT >>> " + packageNameForAPT);
        }

        // 必传参数判空(乱码问题:添加java控制台输出中文乱码)
        if (EmptyUtils.isEmpty(moduleName) || EmptyUtils.isEmpty(packageNameForAPT)) {
            throw new RuntimeException("注解处理器需要的参数moduleName或者packageName为空,请在对应build.gradle配置参数");
        }
    }
}

遍历所有ARouter节点

@Override
public boolean process(Set set, RoundEnvironment roundEnvironment) {
    // 一旦有类之上使用@ARouter注解
    if (!EmptyUtils.isEmpty(set)) {
        // 获取所有被 @ARouter 注解的 元素集合
        Set elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);

        if (!EmptyUtils.isEmpty(elements)) {
            // 解析元素
            try {
                parseElements(elements);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 坑:必须写返回值,表示处理@ARouter注解完成
        return true;
    }
    return false;
}

解析元素

// 解析所有被 @ARouter 注解的 类元素集合
private void parseElements(Set elements) throws IOException {
    // 通过Element工具类,获取Activity、Callback类型
    TypeElement activityType = elementUtils.getTypeElement(Constants.ACTIVITY);

    // 显示类信息(获取被注解节点,类节点)这里也叫自描述 Mirror
    TypeMirror activityMirror = activityType.asType();

    // 遍历节点
    for (Element element : elements) {
        // 获取每个元素类信息,用于比较
        TypeMirror elementMirror = element.asType();
        messager.printMessage(Diagnostic.Kind.NOTE, "遍历元素信息:" + elementMirror.toString());

        // 获取每个类上的@ARouter注解中的注解值
        ARouter aRouter = element.getAnnotation(ARouter.class);

        // 路由详细信息,最终实体封装类
        RouterBean bean = new RouterBean.Builder()
                .setGroup(aRouter.group())
                .setPath(aRouter.path())
                .setElement(element)
                .build();

        // 高级判断:ARouter注解仅能用在类之上,并且是规定的Activity
        // 类型工具类方法isSubtype,相当于instance一样
        if (typeUtils.isSubtype(elementMirror, activityMirror)) {
            bean.setType(RouterBean.Type.ACTIVITY);
        } else {
            // 不匹配抛出异常,这里谨慎使用!考虑维护问题
            throw new RuntimeException("@ARouter注解目前仅限用于Activity类之上");
        }

        // 赋值临时map存储,用来存放路由组Group对应的详细Path类对象
        valueOfPathMap(bean);
    }

    // routerMap遍历后,用来生成类文件

    // 获取ARouterLoadGroup、ARouterLoadPath类型(生成类文件需要实现的接口)
    TypeElement groupLoadType = elementUtils.getTypeElement(Constants.AROUTE_GROUP); // 组接口
    TypeElement pathLoadType = elementUtils.getTypeElement(Constants.AROUTE_PATH); // 路径接口

    // 第一步:生成路由组Group对应详细Path类文件,如:ARouter$$Path$$app
    createPathFile(pathLoadType);

    // 第二步:生成路由组Group类文件(没有第一步,取不到类文件),如:ARouter$$Group$$app
    createGroupFile(groupLoadType, pathLoadType);
}

/**
 * 生成路由组Group对应详细Path,如:ARouter$$Path$$app
 *
 * @param pathLoadType ARouterLoadPath接口信息
 */
private void createPathFile(TypeElement pathLoadType) throws IOException {
    // 判断是否有需要生成的类文件
    if (EmptyUtils.isEmpty(tempPathMap)) return;

    TypeName methodReturns = ParameterizedTypeName.get(
            ClassName.get(Map.class), // Map
            ClassName.get(String.class), // Map
    );

    // 遍历分组,每一个分组创建一个路径类文件,如:ARouter$$Path$$app
    for (Map.Entry> entry : tempPathMap.entrySet()) {
        // 方法配置:public Map loadPath() {
        MethodSpec.Builder methodBuidler = MethodSpec.methodBuilder(Constants.PATH_METHOD_NAME) // 方法名
                .addAnnotation(Override.class) // 重写注解
                .addModifiers(Modifier.PUBLIC) // public修饰符
                .returns(methodReturns); // 方法返回值

        // 遍历之前:Map pathMap = new HashMap<>();
        methodBuidler.addStatement("$T<$T, $T> $N = new $T<>()",
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouterBean.class),
                Constants.PATH_PARAMETER_NAME,
                HashMap.class);

        // 一个分组,如:ARouter$$Path$$app。有很多详细路径信息,如:/app/MainActivity、/app/OtherActivity
        List pathList = entry.getValue();
        // 方法内容配置(遍历每个分组中每个路由详细路径)
        for (RouterBean bean : pathList) {
            // 类似String.format("hello %s net163 %d", "net", 163)通配符
            // pathMap.put("/app/MainActivity", RouterBean.create(
            //        RouterBean.Type.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));
            methodBuidler.addStatement(
                    "$N.put($S, $T.create($T.$L, $T.class, $S, $S))",
                    Constants.PATH_PARAMETER_NAME, // pathMap.put
                    bean.getPath(), // "/app/MainActivity"
                    ClassName.get(RouterBean.class), // RouterBean
                    ClassName.get(RouterBean.Type.class), // RouterBean.Type
                    bean.getType(), // 枚举类型:ACTIVITY
                    ClassName.get((TypeElement) bean.getElement()), // MainActivity.class
                    bean.getPath(), // 路径名
                    bean.getGroup() // 组名
            );
        }

        // 遍历之后:return pathMap;
        methodBuidler.addStatement("return $N", Constants.PATH_PARAMETER_NAME);

        // 最终生成的类文件名
        String finalClassName = Constants.PATH_FILE_NAME + entry.getKey();
        messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由Path类文件:" +
                packageNameForAPT + "." + finalClassName);

        // 生成类文件:ARouter$$Path$$app
        JavaFile.builder(packageNameForAPT, // 包名
                TypeSpec.classBuilder(finalClassName) // 类名
                        .addSuperinterface(ClassName.get(pathLoadType)) // 实现ARouterLoadPath接口
                        .addModifiers(Modifier.PUBLIC) // public修饰符
                        .addMethod(methodBuidler.build()) // 方法的构建(方法参数 + 方法体)
                        .build()) // 类构建完成
                .build() // JavaFile构建完成
                .writeTo(filer); // 文件生成器开始生成类文件

        // 非常重要一步!!!!!路径文件生成出来了,才能赋值路由组tempGroupMap
        tempGroupMap.put(entry.getKey(), finalClassName);
    }
}

/**
 * 生成路由组Group文件,如:ARouter$$Group$$app
 *
 * @param groupLoadType ARouterLoadGroup接口信息
 * @param pathLoadType ARouterLoadPath接口信息
 */
private void createGroupFile(TypeElement groupLoadType, TypeElement pathLoadType) throws IOException {
    // 判断是否有需要生成的类文件
    if (EmptyUtils.isEmpty(tempGroupMap) || EmptyUtils.isEmpty(tempPathMap)) return;

    TypeName methodReturns = ParameterizedTypeName.get(
            ClassName.get(Map.class), // Map
            ClassName.get(String.class), // Map
            // 某某Class是否属于ARouterLoadPath接口的实现类
            ParameterizedTypeName.get(ClassName.get(Class.class),
                    WildcardTypeName.subtypeOf(ClassName.get(pathLoadType)))
    );

    // 方法配置:public Map> loadGroup() {
    MethodSpec.Builder methodBuidler = MethodSpec.methodBuilder(Constants.GROUP_METHOD_NAME) // 方法名
            .addAnnotation(Override.class) // 重写注解
            .addModifiers(Modifier.PUBLIC) // public修饰符
            .returns(methodReturns); // 方法返回值

    // 遍历之前:Map> groupMap = new HashMap<>();
    methodBuidler.addStatement("$T<$T, $T> $N = new $T<>()",
            ClassName.get(Map.class),
            ClassName.get(String.class),
            ParameterizedTypeName.get(ClassName.get(Class.class),
                    WildcardTypeName.subtypeOf(ClassName.get(pathLoadType))),
            Constants.GROUP_PARAMETER_NAME,
            HashMap.class);

    // 方法内容配置
    for (Map.Entry entry : tempGroupMap.entrySet()) {
        // 类似String.format("hello %s net163 %d", "net", 163)通配符
        // groupMap.put("main", ARouter$$Path$$app.class);
        methodBuidler.addStatement("$N.put($S, $T.class)",
                Constants.GROUP_PARAMETER_NAME, // groupMap.put
                entry.getKey(),
                // 类文件在指定包名下
                ClassName.get(packageNameForAPT, entry.getValue()));
    }

    // 遍历之后:return groupMap;
    methodBuidler.addStatement("return $N", Constants.GROUP_PARAMETER_NAME);

    // 最终生成的类文件名
    String finalClassName = Constants.GROUP_FILE_NAME + moduleName;
    messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由组Group类文件:" +
            packageNameForAPT + "." + finalClassName);

    // 生成类文件:ARouter$$Group$$app
    JavaFile.builder(packageNameForAPT, // 包名
            TypeSpec.classBuilder(finalClassName) // 类名
                    .addSuperinterface(ClassName.get(groupLoadType)) // 实现ARouterLoadGroup接口
                    .addModifiers(Modifier.PUBLIC) // public修饰符
                    .addMethod(methodBuidler.build()) // 方法的构建(方法参数 + 方法体)
                    .build()) // 类构建完成
            .build() // JavaFile构建完成
            .writeTo(filer); // 文件生成器开始生成类文件
}

/**
 * 赋值临时map存储,用来存放路由组Group对应的详细Path类对象,生成路由路径类文件时遍历
 *
 * @param bean 路由详细信息,最终实体封装类
 */
private void valueOfPathMap(RouterBean bean) {
    if (checkRouterPath(bean)) {
        messager.printMessage(Diagnostic.Kind.NOTE, "RouterBean >>> " + bean.toString());

        // 开始赋值Map
        List routerBeans = tempPathMap.get(bean.getGroup());
        // 如果从Map中找不到key为:bean.getGroup()的数据,就新建List集合再添加进Map
        if (EmptyUtils.isEmpty(routerBeans)) {
            routerBeans = new ArrayList<>();
            routerBeans.add(bean);
            tempPathMap.put(bean.getGroup(), routerBeans);
        } else { // 找到了key,直接加入List集合
            routerBeans.add(bean);
        }

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

/**
 * 校验@ARouter注解的值,如果group未填写就从必填项path中截取数据
 *
 * @param bean 路由详细信息,最终实体封装类
 */
private boolean checkRouterPath(RouterBean bean) {
    String group = bean.getGroup();
    String path = bean.getPath();

    // @ARouter注解中的path值,必须要以 / 开头(模仿阿里Arouter规范)
    if (EmptyUtils.isEmpty(path) || !path.startsWith("/")) {
        messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的path值,必须要以 / 开头");
        return false;
    }

    // 比如开发者代码为:path = "/MainActivity",最后一个 / 符号必然在字符串第1位
    if (path.lastIndexOf("/") == 0) {
        // 架构师定义规范,让开发者遵循
        messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解未按规范配置,如:/app/MainActivity");
        return false;
    }

    // 从第一个 / 到第二个 / 中间截取,如:/app/MainActivity 截取出 app 作为group
    String finalGroup = path.substring(1, path.indexOf("/", 1));

    // @ARouter注解中的group有赋值情况
    if (!EmptyUtils.isEmpty(group) && !group.equals(moduleName)) {
        // 架构师定义规范,让开发者遵循
        messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter注解中的group值必须和子模块名一致!");
        return false;
    } else {
        bean.setGroup(finalGroup);
    }

    return true;
}

你可能感兴趣的:(架构师(六)——组件化路由架构设计)