第一步,在要跳转的 activity 上面注明 path,
@Route(path = “activity/main”)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
在要跳转的地方
Router.getInstance().build(“activity/main”).navigation(this);
第一步,使用 @Modules({"app", "sdk"})
注明总共有多少个 moudle,并分别在 moudle 中注明当前 moudle 的 名字,使用 @Module("")
注解。注意 @Modules({“app”, “sdk”}) 要与 @Module("") 一一对应。
在主 moudle 中,
@Modules({“app”, “moudle1”})
@Module(“app”)
public class RouterApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Router.getInstance().init();
}
}
在 moudle1 中,
@Route(path = “my/activity/main”)
@Module(“moudle1”)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_2);
}
}
这样就可以支持多模块使用了。
Router.getInstance().add(“activity/three”, ThreeActivity.class);
跳转的时候调用
Router.getInstance().build(“activity/three”).navigation(this);
路由跳转结果回调。
Router.getInstance().build(“my/activity/main”, new RouterCallback() {
@Override
public boolean beforeOpen(Context context, Uri uri) {
// 在打开路由之前
Log.i(TAG, “beforeOpen: uri=” + uri);
return false;
}
// 在打开路由之后(即打开路由成功之后会回调)
@Override
public void afterOpen(Context context, Uri uri) {
Log.i(TAG, “afterOpen: uri=” + uri);
}
// 没有找到改 uri
@Override
public void notFind(Context context, Uri uri) {
Log.i(TAG, “notFind: uri=” + uri);
}
// 发生错误
@Override
public void error(Context context, Uri uri, Throwable e) {
Log.i(TAG, “error: uri=” + uri + “;e=” + e);
}
}).navigation(this);
startActivityForResult 跳转结果回调
Router.getInstance().build(“activity/two”).navigation(this, new Callback() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, “onActivityResult: requestCode=” + requestCode + “;resultCode=” + resultCode + “;data=” + data);
}
});
实现一个 Router 框架,涉及到的主要的知识点如下:
我们带着这三个问题,一起来探索一下。
总共分为四个部分,router-annotion, router-compiler,router-api,stub
router-annotion 主要是定义注解的,用来存放注解文件
router-compiler 主要是用来处理注解的,自动帮我们生成代码
router-api 是对外的 api,用来处理跳转的。
stub 这个是存放一些空的 java 文件,提前占坑。不会打包进 jar。
主要定义了三个注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path();
}
@Retention(RetentionPolicy.CLASS)
public @interface Modules {
String[] value();
}
@Retention(RetentionPolicy.CLASS)
public @interface Module {
String value();
}
Route 注解主要是用来注明跳转的 path 的。
Modules 注解,注明总共有多少个 moudle。
Module 注解,注明当前 moudle 的名字。
Modules,Module 注解主要是为了解决支持多 module 使用的。
router-compiler 只有一个类 RouterProcessor,他的原理其实也是比较简单的,扫描那些类用到注解,并将这些信息存起来,做相应的处理。这里是会生成相应的 java 文件。
主要包括以下两个步骤
@Modules
@Module
注解,然后生成相应的 RouterInit
文件@Route
注解,并根据 moudleName
生成相应的 java 文件在讲解 RouterProcessor 之前,我们先来了解一下注解的基本知识。
如果对于自定义注解还不熟悉的话,可以先看我之前写的这两篇文章。Android 自定义编译时注解1 - 简单的例子,Android 编译时注解 —— 语法详解
public class RouterProcessor extends AbstractProcessor {
private static final boolean DEBUG = true;
private Messager messager;
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
mFiler = processingEnv.getFiler();
UtilManager.getMgr().init(processingEnv);
}
/**
/**
}
首先我们先来看一下 getSupportedAnnotationTypes
方法,这个方法返回的是我们支持扫描的注解。
接下来我们再一起来看一下 process
方法
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 注解为 null,直接返回
if (annotations == null || annotations.size() == 0) {
return false;
}
UtilManager.getMgr().getMessager().printMessage(Diagnostic.Kind.NOTE, “process”);
boolean hasModule = false;
boolean hasModules = false;
// module
String moduleName = “RouterMapping”;
Set extends Element> moduleList = roundEnv.getElementsAnnotatedWith(Module.class);
if (moduleList != null && moduleList.size() > 0) {
Module annotation = moduleList.iterator().next().getAnnotation(Module.class);
moduleName = moduleName + “_” + annotation.value();
hasModule = true;
}
// modules
String[] moduleNames = null;
Set extends Element> modulesList = roundEnv.getElementsAnnotatedWith(Modules.class);
if (modulesList != null && modulesList.size() > 0) {
Element modules = modulesList.iterator().next();
moduleNames = modules.getAnnotation(Modules.class).value();
hasModules = true;
}
debug(“generate modules RouterInit annotations=” + annotations + " roundEnv=" + roundEnv);
debug(“generate modules RouterInit hasModules=” + hasModules + " hasModule=" + hasModule);
// RouterInit
if (hasModules) { // 有使用 @Modules 注解,生成 RouterInit 文件,适用于多个 moudle
debug(“generate modules RouterInit”);
generateModulesRouterInit(moduleNames);
} else if (!hasModule) { // 没有使用 @Modules 注解,并且有使用 @Module,生成相应的 RouterInit 文件,使用与单个 moudle
debug(“generate default RouterInit”);
generateDefaultRouterInit();
}
// 扫描 Route 注解
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
List targetInfos = new ArrayList<>();
for (Element element : elements) {
System.out.println(“elements =” + elements);
// 检查类型
if (!Utils.checkTypeValid(element)) continue;
TypeElement typeElement = (TypeElement) element;
Route route = typeElement.getAnnotation(Route.class);
targetInfos.add(new TargetInfo(typeElement, route.path()));
}
// 根据 module 名字生成相应的 java 文件
if (!targetInfos.isEmpty()) {
generateCode(targetInfos, moduleName);
}
return false;
}
,首先判断是否有注解需要处理,没有的话直接返回 annotations == null || annotations.size() == 0
。
接着我们会判断是否有 @Modules
注解(这种情况是多个 moudle 使用),有的话会调用 generateModulesRouterInit(String[] moduleNames)
方法生成 RouterInit java 文件,当没有 @Modules
注解,并且没有 @Module
(这种情况是单个 moudle 使用),会生成默认的 RouterInit 文件。
private void generateModulesRouterInit(String[] moduleNames) {
MethodSpec.Builder initMethod = MethodSpec.methodBuilder(“init”)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC);
for (String module : moduleNames) {
initMethod.addStatement(“RouterMapping_” + module + “.map()”);
}
TypeSpec routerInit = TypeSpec.classBuilder(“RouterInit”)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(initMethod.build())
.build();
try {
JavaFile.builder(Constants.ROUTE_CLASS_PACKAGE, routerInit)
.build()
.writeTo(mFiler);
} catch (Exception e) {
e.printStackTrace();
}
}
假设说我们有"app",“moudle1” 两个 moudle,那么我们最终生成的代码是这样的。
public final class RouterInit {
public static final void init() {
RouterMapping_app.map();
RouterMapping_moudle1.map();
}
}
如果我们都没有使用 @Moudles 和 @Module 注解,那么生成的 RouterInit 文件大概是这样的。
public final class RouterInit {
public static final void init() {
RouterMapping.map();
}
}
这也就是为什么有 stub module 的原因。因为默认情况下,我们需要借助 RouterInit 去初始化 map。如果没有这两个文件,ide 编辑器 在 compile 的时候就会报错。
compileOnly project(path: ‘:stub’)
我们引入的方式是使用 compileOnly,这样的话再生成 jar 的时候,不会包括这两个文件,但是可以在 ide 编辑器中运行。这也是一个小技巧。
我们回过来看 process 方法连对 Route 注解的处理。
// 扫描 Route 自己注解
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
List targetInfos = new ArrayList<>();
for (Element element : elements) {
System.out.println(“elements =” + elements);
// 检查类型
if (!Utils.checkTypeValid(element)) continue;
TypeElement typeElement = (TypeElement) element;
Route route = typeElement.getAnnotation(Route.class);
targetInfos.add(new TargetInfo(typeElement, route.path()));
}
// 根据 module 名字生成相应的 java 文件
更多学习和讨论,欢迎加入我们的知识星球!
点击这里加入我们吧!
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
-mZQO3CTX-1646141029026)]
更多学习和讨论,欢迎加入我们的知识星球!
点击这里加入我们吧!
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
这里有2000+小伙伴,让你的学习不寂寞~·