项目地址RouterDemo:https://github.com/532268948/RouterDemo
在组件化开发中一个必须要面对的问题就是组件间页面跳转,实现的方法有很多,简单的可以通过反射获取,但是比较耗费性能,也可以通过隐式跳转,但是随着页面的增多,过滤条件会随之增多,后期维护麻烦。那还有什么方法呢,没错,就是接下来要介绍的Arouter路由框架,该框架是阿里巴巴开源项目,大厂出品,必属精品。使用过Arouter得同学都知道Arouter是通过给每个页面添加@Route注解然后调用一定的方法实现跳转的,而Arouter的核心就是这个注解。
这里要介绍一个概念,APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它用来在编译时扫描和处理注解,注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件。
Arouter的路由表就是在该工具下在编译期生成的,说简单了,就是利用注解在编译期生成了一些java文件,我们在这些新的java文件中将所有被注解的页面添加进了路由表中。
arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译器把注解标注的相关目标类生成映射文件,包含路由框架所使用的全部注解,及其相关类
arouter-api:实现路由控制
新建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
}
annotationProcessor project(':router_compiler')
api project(':router_compiler')
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
public @interface Route {
/**
* Path of route
*/
String path();
}
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;
}
}
public interface IRoute {
/**
*
* @param routes 模块下的路由集合
*/
void loadInto(Map<String, RouteMeta> routes);
}
/**
* @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;
}
}
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()));
}
}
}
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Router.init(this);
}
}
在AndroidManifest文件的application节点添加下列语句
android:name=".MyApplication"
以下代码是编译器自动生成的
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