Android 组件化路由框架设计(仿Arouter)

Android 组件化路由框架设计(仿Arouter)


项目地址RouterDemo:https://github.com/532268948/RouterDemo

一、前言

  在组件化开发中一个必须要面对的问题就是组件间页面跳转,实现的方法有很多,简单的可以通过反射获取,但是比较耗费性能,也可以通过隐式跳转,但是随着页面的增多,过滤条件会随之增多,后期维护麻烦。那还有什么方法呢,没错,就是接下来要介绍的Arouter路由框架,该框架是阿里巴巴开源项目,大厂出品,必属精品。使用过Arouter得同学都知道Arouter是通过给每个页面添加@Route注解然后调用一定的方法实现跳转的,而Arouter的核心就是这个注解。
  这里要介绍一个概念,APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它用来在编译时扫描和处理注解,注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件。
  Arouter的路由表就是在该工具下在编译期生成的,说简单了,就是利用注解在编译期生成了一些java文件,我们在这些新的java文件中将所有被注解的页面添加进了路由表中。

Android 组件化路由框架设计(仿Arouter)_第1张图片

二、设计思路

arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译器把注解标注的相关目标类生成映射文件,包含路由框架所使用的全部注解,及其相关类
arouter-api:实现路由控制

Android 组件化路由框架设计(仿Arouter)_第2张图片

三、实现效果

Android 组件化路由框架设计(仿Arouter)_第3张图片

四、步骤

  • 新建module java library(router_compiler)
Android 组件化路由框架设计(仿Arouter)_第4张图片 Android 组件化路由框架设计(仿Arouter)_第5张图片
  • 新建module android library(router_api),步骤如上,但是要注意选择Android library。

  • 在route-compiler的gradle文件中导入依赖和jdk版本支持

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    api 'com.squareup:javapoet:1.11.1'
    api 'org.apache.commons:commons-collections4:4.4'
    api 'org.apache.commons:commons-lang3:3.5'
}

sourceCompatibility = "8"
targetCompatibility = "8"
  • 在app工程gradle文件中添加下列语句
compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
}
  • 在route_api和app模块等其他组件化模块的gradle文件中导入route_compiler模块
annotationProcessor project(':router_compiler')
api project(':router_compiler')
  • 在每个module模块中的gradle文件中添加下列语句用来获取每个module的包名
javaCompileOptions {
    annotationProcessorOptions {
        arguments = [ROUTER_MODULE_NAME: project.getName()]
    }
}
  • 在router_compiler模块中创建RouteProcessor类并继承自AbstractProcessor

  • 在router_compiler模块中的main文件夹下创建文件夹resources/META-INF/services,然后创建javax.annotation.processing.Processor文件,并添加下列语句

com.nsyw.routerdemo.router_compiler.RouteProcessor
Android 组件化路由框架设计(仿Arouter)_第6张图片
  • 在router-compiler根目录下新建注解类Route
public @interface Route {

    /**
     * Path of route
     */
    String path();

}
  • 在router-compiler根目录下新建RouteMeta用于存储路由信息,RouteType是枚举类,表示注解的类型。
public class RouteMeta {

    /**
     * 路径名称
     */
    private String path;
    /**
     * 路由类型 ,现在只考虑Activity
     */
    private RouteType routeType;
    /**
     * 注解的Activity的类
     */
    private Class clazz;

    public RouteMeta(String path, RouteType routeType) {
        this.path = path;
        this.routeType = routeType;
    }

    public RouteMeta(String path, RouteType routeType, Class clazz) {
        this.path = path;
        this.routeType = routeType;
        this.clazz = clazz;
    }

    public String getPath() {
        return path;
    }

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

    public RouteType getRouteType() {
        return routeType;
    }

    public void setRouteType(RouteType routeType) {
        this.routeType = routeType;
    }

    public Class getClazz() {
        return clazz;
    }

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

    public static RouteMeta build(String path, RouteType routeType, Class clazz) {
        return new RouteMeta(path, routeType, clazz);
    }
}
public enum RouteType {
    ACTIVITY(0, "android.app.Activity"),
    UNKNOWN(-1, "Unknown route type");

    int id;
    String className;

    public int getId() {
        return id;
    }

    public RouteType setId(int id) {
        this.id = id;
        return this;
    }

    public String getClassName() {
        return className;
    }

    public RouteType setClassName(String className) {
        this.className = className;
        return this;
    }

    RouteType(int id, String className) {
        this.id = id;
        this.className = className;
    }
}
  • 创建接口IRoute
public interface IRoute {

    /**
     *
     * @param routes 模块下的路由集合
     */
    void loadInto(Map<String, RouteMeta> routes);
}
  • RouteProcessor.java
/**
 * @SupportedAnnotationTypes表示支持的注解类型
 */
@SupportedAnnotationTypes("com.nsyw.routerdemo.router_compiler.annotation.Route")
public class RouteProcessor extends AbstractProcessor {
    private Filer mFiler;
    private Types types;
    private Elements mElementsUtil;

    /**
     * 当前App的包名
     */
    private String moduleName;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
        types = processingEnv.getTypeUtils();
        mElementsUtil = processingEnvironment.getElementUtils();
        // 获取当前Application的包名
        Map<String, String> options = processingEnv.getOptions();
        if (MapUtils.isNotEmpty(options)) {
            moduleName = options.get(KEY_MODULE_NAME);
        }
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Set<? extends Element> routeElements = roundEnvironment.getElementsAnnotatedWith(Route.class);
        if (CollectionUtils.isNotEmpty(routeElements)) {

            TypeMirror type_Activity = mElementsUtil.getTypeElement(ACTIVITY).asType();

            /*
              参数类型Map
             */
            ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                    ClassName.get(Map.class),
                    ClassName.get(String.class),
                    ClassName.get(RouteMeta.class)
            );

            ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "routes").build();

            /*
              methodBuilder 方法名
              addAnnotation 方法添加注解
              addModifiers  方法访问限制类型
              addParameter  添加参数
             */
            MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(groupParamSpec);

            ClassName routeMetaCn = ClassName.get(RouteMeta.class);
            ClassName routeTypeCn = ClassName.get(RouteType.class);

            //遍历@Route注解的所有Activity
            for (Element element : routeElements) {
                TypeMirror tm = element.asType();
                //获取注解
                Route route = element.getAnnotation(Route.class);
                RouteMeta routeMeta = null;
                if (types.isSubtype(tm, type_Activity)) {
                    routeMeta = new RouteMeta(route.path(), RouteType.ACTIVITY);
                }

                //获取被注解的类的类名
                ClassName className = ClassName.get((TypeElement) element);

                /*
                  方法内的添加路由语句
                  routes.put(routeMeta.getPath(),RouteMeta.build(routeMeta.getPath(),RouteType.ACTIVITY,className.class))
                 */
                loadIntoMethodOfGroupBuilder.addStatement(
                        "routes.put($S,$T.build($S,$T." + routeMeta.getRouteType() + ", $T.class))",
                        routeMeta.getPath(),
                        routeMetaCn,
                        routeMeta.getPath(),
                        routeTypeCn,
                        className);
            }
            /*
              构建java文件
             */
            try {
                JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                        TypeSpec.classBuilder(NAME_OF_ROUTE + moduleName)
                                .addJavadoc(WARNING_TIPS)
                                .addSuperinterface(ClassName.get(mElementsUtil.getTypeElement(IROUTE_LOAD)))
                                .addModifiers(PUBLIC)
                                .addMethod(loadIntoMethodOfGroupBuilder.build())
                                .build()
                ).build().writeTo(mFiler);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return true;
        }
        return false;
    }
}
  • 在router_api模块下创建Router
public class Router {

    private static volatile Router mInstance = new Router();
    private Context mContext;
    private String path;
    private Map<String, RouteMeta> map = new HashMap<>();

    public static void init(Application application) {
        mInstance.mContext = application;
        Set<String> routerMap;
        try {
            routerMap = ClassUtils.getFileNameByPackageName(mInstance.mContext, consts.PACKAGE_OF_GENERATE_FILE);
            Log.e("Router", routerMap.toString());
            for (String className : routerMap) {
                ((IRoute) (Class.forName(className).getConstructor().newInstance())).loadInto(mInstance.map);
            }
        } catch (PackageManager.NameNotFoundException | InterruptedException | IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    public static synchronized Router getInstance() {
        return mInstance;
    }

    public Router build(String path) {
        mInstance.path = path;
        return mInstance;
    }

    public void navigation(Context context) {
        RouteMeta routeMeta = mInstance.map.get(mInstance.path);
        if (routeMeta != null) {
            context.startActivity(new Intent(context, routeMeta.getClazz()));
        }
    }
}
  • Router需要在Application中初始化
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Router.init(this);
    }
}

在AndroidManifest文件的application节点添加下列语句

android:name=".MyApplication"
  • 项目build的完之后会在各个模块的相应的文件夹下生成java文件,这些文件会被Router依次获取将路由信息存入路由表中。
    Android 组件化路由框架设计(仿Arouter)_第7张图片

以下代码是编译器自动生成的

package com.nsyw.routerdemo.routes;

import com.nsyw.routerdemo.MainOneActivity;
import com.nsyw.routerdemo.MainTwoActivity;
import com.nsyw.routerdemo.router_compiler.IRoute;
import com.nsyw.routerdemo.router_compiler.RouteMeta;
import com.nsyw.routerdemo.router_compiler.RouteType;
import java.lang.Override;
import java.lang.String;
import java.util.Map;

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class Router$$App$$app implements IRoute {
  @Override
  public void loadInto(Map<String, RouteMeta> routes) {
    routes.put("/main/one",RouteMeta.build("/main/one",RouteType.ACTIVITY, MainOneActivity.class));
    routes.put("/main/two",RouteMeta.build("/main/two",RouteType.ACTIVITY, MainTwoActivity.class));
  }
}
  • 使用
 Router.getInstance().build("/main/one").navigation(MainActivity.this);

小结

Router只是参照ARouter手动实现的路由框架,剔除掉了很多东西,只实现了组件间Activity之间的跳转,如果想要用在项目里,建议还是用ARouter更好,毕竟这只是个练手项目,功能也不够全面,当然有同学想对demo扩展后使用那当然更好,遇到什么问题可以及时联系我。我的目的是通过自己手动实现路由框架来加深对知识的理解,如这里面涉及到的知识点apt、javapoet和组件化思路、编写框架的思路等。看到这里,如果感觉干货很多,欢迎关注我的github,里面会有更多干货!

项目地址RouterDemo:https://github.com/532268948/RouterDemo

你可能感兴趣的:(组件化,android,路由)