从零到一完成一个健身App之二:路由框架设计
1.路由框架的相关背景
路由框架是组件化大背景下出现的一种成熟方案。
相信有过组件化开发经验的同学都会知道,如果我们想在Module A中打开Module B中的Activity,这时候的Activity是找不到引用的。显式跳转是行不通的。另外,在很多时候我们都需要后台来动态确定我们需要跳转到什么页面。比如常见的分享码等。除此之外,H5页面越来越多,而H5页面是没办法通过startActivity跳到原生页面的。所以在这种情况下需要定义一种更适合组件化的,更灵活的路由方式。
为了找到这种方式,我们经历了以下过程
- 隐式意图Activity跳转:依赖于Manifest文件的修改,并且参数不方便传递。使用了startActivity之后就无法插手任何环节了,就无法在跳转失败的时候降级。
- 基于事件,广播或EventBus,这种情况下跳转流不容易监控,而且在跳转复杂的情况下接入维护成本较高
- 调用一个固定的方法:这种情况侵入性太强,所有Activity都要实现,改造起来困难,难于扩展。
所以我们需要一个优秀的路由框架,要能够实现:
- 通过与后台一起定义schema,可以达到按后台所需跳转到特定页面的需求,也可以和H5页面统一跳转方式
- 代码侵入性弱,调用方便。
- 接入方简单易用。
- 灵活,能针对特定需求进行特定的处理。比如我们常见的登录判断和权限检查等。
具体到实现方面,要能做到:
- 路由注册采用apt注解式自动生成,避免手动管理
- 参数依赖注入,自动保存,不再需要手动写onSaveInstance、onCreate(SaveInstace)、onNewIntent(Intent)、getQueryParamer等
- 能动态拦截和动态替换
2.现有框架调研
当前比较知名的路由框架有:阿里的ARouter,美团的WMRouter,ActivityRouter.
2.1 ARouter
ARouter有多个优势:
1.直接解析URL路由,解析参数并赋值到对应目标字段的页面中;
2.支持多模块项目;
3.支持InstantRun;
4.拦截器策略,允许自定义;
5.提供IoC容器,控制反转;
6.映射关系自动注册;
7.灵活的降级策略.
赶紧学习一下这些都是怎么做到的。先看一个示例1
2
3
4"/test/activity2") (path =
public class Test2Activity extends AppCompatActivity
ARouter.getInstance().build("/test/activity2")..withString("key1", "value1").navigation();
再看看ARouter的架构。
其中Compiler中三个处理器,分别是:
Route Processor:处理路径路由
Interceptor Processor:处理拦截器
Autowire Processor:进行自动装配
API中Launcher是用户可以调用的api所在的地方
Service是将功能和组件封装成的接口,对外开放。等到时候分析源码的时候要注意一下是怎么做到的。
Templete是用于SDK编译器生成映射文件时候提供的模板。
更下层的ware House: 存储了ARouter在运行期间加载的一些配置文件以及映射关系
Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的
Class工具则是用于解决不同类型APK的兼容问题的。
再下一层就是Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层, 也按功能模块划分。
一.如何实现解析URL路由,解析参数并赋值到目标字段中?
ARouter采用的是APT技术,通过定义的annotation来解析到相应的path。这里会遇到的第一个问题是找到处理注解的时机。运行期处理注解会大量的运用反射。所以要在编译期处理被注解的类。至于如何区分,就在于这里:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/* 用来指明注解的访问范围
* 1.源码级注解SOURCE,该类型的注解信息会留在.java源码中,
* 源码编译后,注解信息会被丢弃,不会保留在编译好的.class文件中;
* 2.编译时注解CLASS,注解信息会保留在.java源码里和.class文件中,
* 在执行的时候,会被Java虚拟机丢弃不回家再到虚拟机中;
* 3.运行时注解RUNTIME,java源码里,.class文件中和Java虚拟机在运行期也保留注解信息,
* 可通过反射读取
*/
(RUNTIME)
//是一个ElementType类型的数组,用来指定注解所使用的对象范围
(value = FIELD)
public Add {
float ele1() default 0f;
float ele2() default 0f;
}