前言
在组件化开发中一个必须要面对的问题就是组件间页面跳转,实现的方法有很多,简单的可以通过反射获取,但是比较耗费性能,也可以通过隐式跳转,但是随着页面的增多,过滤条件会随之增多,后期维护麻烦。那还有什么方法呢,没错,就是接下来要介绍的Arouter路由框架,该框架是阿里巴巴开源项目,大厂出品,必属精品。使用过Arouter得同学都知道Arouter是通过给每个页面添加@Route注解然后调用一定的方法实现跳转的,而Arouter的核心就是这个注解。
??这里要介绍一个概念,APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它用来在编译时扫描和处理注解,注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件。
??Arouter的路由表就是在该工具下在编译期生成的,说简单了,就是利用注解在编译期生成了一些java文件,我们在这些新的java文件中将所有被注解的页面添加进了路由表中。
设计思路
arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译器把注解标注的相关目标类生成映射文件,包含路由框架所使用的全部注解,及其相关类
arouter-api:实现路由控制
实现效果
步骤
- 新建module java library(router_compiler)(因为在主 Module 中无法找到 AbstractProcessor 类)
- 新建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"
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
- 在router-compiler根目录下新建注解类Route
public @interface Route {
/**
* Path of route
*/
String path();
}
- 创建接口IRoute,自动生成的java文件都要继承自该接口
public interface IRoute {
/**
*
* @param routes 模块下的路由集合
*/
void loadInto(Map routes);
}
- RouteProcessor.java
/**
* @SupportedAnnotationTypes表示支持的注解类型
*/
@SupportedAnnotationTypes("com.nsyw.routerdemo.router_compiler.annotation.Route")
public class RouteProcessor extends AbstractProcessor {
......
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
......
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
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 map = new HashMap<>();
public static void init(Application application) {
mInstance.mContext = application;
Set 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依次获取将路由信息存入路由表中。
以下代码是编译器自动生成的
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 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,里面会有更多干货!