本篇博文基于自己实现的路由组件,主要功能包括Activity路由跳转,支持自定义服务。代码实现比较简单,重在探讨路由组件的核心原理,如需品尝功能更全、代码更屌的框架,可以直接前往ARouter,下载源码,即可。
一、核心原理
还是那句话,没什么是一张图解决不了的问题,如果有,那就是两张。O(∩_∩)O
路由组件的核心原理,如上图所示:
共分为两大部分:编译时 + 运行时。
a)编译时
1、路由信息收集
路由信息收集的前提是有数据源,而这个数据源就是我们自定义的编译时注解Route。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
//配置路由,格式:/xxx/yyy,必须以/开头
String path();
}
当然,只是定义一个编译时注解是没用的,需要与之配合的编译时注解处理器来完成相应的处理工作。
使用:
@Route(path = "/main/mainactivity")
public class MainActivity extends AppCompatActivity {
......
}
我们这个所说的路由信息就是”/main/mainactivity“及相关信息,相关信息是指该路由对应的目标类,比如本例的MainActivity;还有目标类的类型,比如本例的是Activity,这样实现跳转时我们知道下一步的动作是startActivity了;携带的参数等等。
主要原理如下:
Java编译时注解框架会在编译时扫描所有的Java源文件,并将带有指定注解的所有元素Element作为参数,传递给我们的注解处理器的process方法进行相应的处理。
注解处理方法的第一步就是收集路由信息。路由这个词意味着分组,不然如何路由呢?因此,在收集路由信息的同时,我们按照一定的规则进行分组 - 比如/main/mainactivity,则属于main分组,同一分组下的所有路由信息就对应了一张路由表。也就是说有多少个分组,后面我们就会生成多少个路由表文件。示例:
/**
* AUTO GENERATED, MODIFY IS USELESS @WRITE BY JSON */
public class app_Group_main implements IRouteGroup {
public void loadRouteInfo(Map<String, RouteMeta> routes) {
routes.put("/main/mainactivity",RouteMeta.build("/main/mainactivity","main",MainActivity.class,TypeEnum.ACTIVITY));
routes.put("/main/shopactivity",RouteMeta.build("/main/shopactivity","main",ShopActivity.class,TypeEnum.ACTIVITY));
}
}
2、路由信息分组、生成路由表文件、路由入口文件
路由信息分组在第一部分已经说了,这里不再说了。
路由信息完成分组之后,下一步的动作就是生成路由表文件,路由表文件包含了同一分组下的所有路由信息,这是一个java文件,文件名可以按照一定的规则来进行命名,比如
app_Group_main,即 module名 + ”_“ + ”Group “+ ”_“ + 分组名。
一个大型的App,路由表文件的数量是非常多的。如果一开始就把所有的路由信息都加载到内存中,这对于本就内存不宽裕的手机设备来说并不是好的做法。比较优秀的做法是只加载用到的路由信息对应的路由表。这就需要借助路由入口文件实现懒加载了。路由入口文件示例如下:
/**
* AUTO GENERATED, MODIFY IS USELESS @WRITE BY JSON */
public class JRouter_Entry_app implements IEntry {
public void loadRouteEntry(Map<String, Class<? extends IRouteGroup>> entries) {
entries.put("test",app_Group_test.class);
entries.put("main",app_Group_main.class);
}
}
我们会在生成路由表文件的同时,生成路由入口文件,如上述代码所示。原因已经说过了,不再详述了。
b)运行时
3、运行时扫描dex文件,加载路由入口信息至内存
在初始化路由组件的时候,我们只会把路由入口文件中的所有信息加载到内存中。
4、运行时,根据路由信息,加载路由表至内存中
在进行具体的路由跳转时,我们才会去加载相应的路由表。比如MainActivity,我们首先会查找路由入口信息,根据分组main找到了对应的路由表文件是app_Group_main.class,则利用反射将app_Group_main的所有路由信息加载到内存中。
具体涉及到运行时扫描dex文件,在后面的源码分析中会详述。
小结:
a)路由生成:收集路由信息 -> 对路由信息分组 -> 每个路由分组生成一个路由表文件 -> 生成路由入口文件;
b)路由跳转:
- [依据分组名]查找路由入口:在路由入口文件查找对应的路由表文件是哪个;
- 加载路由表文件;
- 根据路由path信息,在路由表文件中查找具体的路由信息;
- 拿到足够的信息,进行路由跳转。
二、手写一个ARouter,探究路由实现核心原理
本部分同样按照博客开始的图示,分四个部分实现。
2.1、路由信息收集 & 分组
a)编译时注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
//配置路由