(4.2.40)阿里开源路由框架ARouter的源码分析
来源:互联网 发布:淘宝付款后能退款吗 编辑:程序博客网 时间:2024/06/08 08:43
- 一需求背景
- 1 Android原生方案的不足
- 2 自定义路由框架的适用场景
- 3 对自定义路由框架的设想
- 二ARouter的概述
- 三ARouter的引入和使用
- 四源码分析
- 1 arouter-annotation注解
- 11 Route路由注解
- 12 Interceptor拦截器注解
- 13 Autowired自动装载注解
- 14 RouteMeta路由元信息
- 2 arouter-compiler注解编译器
- 21 Route注解处理
- 211 工程名Group分组名 组内的路由清单列表
- 212 工程名Root模块名 组别的清单列表
- 213 工程名Providers模块名 Ioc的动作路由清单列表
- 22 Autowired注解处理
- 23 Interceptor注解处理
- 21 Route注解处理
- 3 arouter-api路由控制
- 31 ARouter init 初始化过程分析
- 32 ARouter 运行时 API 调用过程分析
- 321 运行期动态修改路由PathReplaceService的实现IOC 也就是IProviderbyType navigationclass的实现方式用于获取 路由目标实例
- 322 一次简单的路由导航
- 323 PostCard对象及LogisticsCentercompletion
- 324 facade分析
- 1 arouter-annotation注解
- 五其他
- 1 Arouter所提供的IOC的单例特性
- 2 关于Compiler中源码的解读
是不是还在困惑 app组件化后如何解决跨module的界面跳转和API调用?
想不想实现运营后台自由配置一个URL下发给app被点击后,想跳转那里就跳转那里?
要不要解决h5网页自由跳转全应用任意界面的需求?
…
那你的确需要阅读本篇文章了
一、需求背景
什么是路由框架?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能。
1.1 Android原生方案的不足
我们所使用的原生路由方案一般是通过显式intent和隐式intent两种方式实现的,均存在一定意义上的缺陷:
- 显式intent,譬如 Intent intent = new Intent(activity, XXActivity.class);
- 由于需要直接持有对应class,从而导致了强依赖关系,提高了耦合度
- 隐式inten,譬如 Intent it = new Intent(); it.setAction(“com.android.activity.MY_ACTION”);
- action等属性的定义在Manifest,导致了扩展性较差
- 规则集中式管理,导致协作变得非常困难
- 原生的路由方案会出现跳转过程无法控制的问题,因为一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会直接抛出运营级的异常
1.2 自定义路由框架的适用场景
前文中我们使用了“一定意义上的缺陷”来描述原生方案的不足,其实也就是说对于大部分的应用场景,Andoird原生路由方案已经足够满足需求了,不需要进行对自定义路由框架的引入
然而,我们也需要意识到自定义路由框架存在的意义:
动态跳转:在一些复杂的业务场景下(比如电商),页面跳转需要较强的灵活性,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,期望根据下发的数据自动的选择页面并进行跳转
组件化:随着业务量的不断增长,app也会不断的膨胀,开发团队的规模和工作量也会逐渐增大,面对所衍生的64K问题、协作开发问题等,app一般都会走向组件化。组件化就是将APP按照一定的功能和业务拆分成多个组件module,不同的组件独立开发,组件化不仅能够提供团队的工作效率,还能够提高应用性能。而组件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系
Native与H5的问题:现在的APP很少是纯Native的,也很少会有纯H5的,一般情况下都是将两者进行结合。这时候就需要非常便捷并且统一的跳转方案,因为在H5中是无法使用StartActivity()跳转到Native页面的,而从Native跳转到H5页面也只能通过配置浏览器的方式实现
- 其他
如果开发团队面临了以上的问题,那么就是时候选择一款实用的自定义路由框架了
当然,如果以上问题并没有成为一定规模,那么使用替代方案实现也无可厚非,但是我们也要意识到他们的不足
- 方案1:文本记录Class全路径,使用反射方式实例Class从而实现跳转
- 运行时反射,会导致性能下降,尤其跳转是一个高发动作
- Class全路径的文本记录会增加维护成本
- 方案2:为所有Activity的配置Action,并文本记录从而实现跳转
- Action的文本记录会增加维护成本
- 无法动态自定义所携带参数
1.3 对自定义路由框架的设想
【图1】
我们期望自定义路由框架能够满足Android原生方案的不足之处,比如:
- 通过URL索引就可以解决类依赖的问题;
- 通过分布式管理页面配置可以解决隐式intent中集中式管理Path的问题;
- 自己实现整个路由过程也可以拥有良好的扩展性
- 通过AOP的方式解决跳转过程无法控制的问题
- 能够提供非常灵活的降级方式
总的来说,一款路由框架至少要满足以下3个功能:
- 分发:把一个URL或者请求按照一定的规则分配给一个服务或者页面来处理,这个流程就是分发,分发是路由框架最基本的功能,当然也可以理解成为简单的跳转。
- 管理:将组件和页面按照一定的规则管理起来,在分发的时候提供搜索、加载、修改等操作,这部分就是管理,也是路由框架的基础,上层功能都是建立在管理之上。
- 控制:就像路由器一样,路由的过程中,会有限速、屏蔽等一些控制操作,路由框架也需要在路由的过程中,对路由操作做一些定制性的扩展,比方刚才提到的AOP,后期的功能更新,也是围绕这个部分来做的。
二、ARouter的概述
ARouter不仅实现了对基础组件的路由,而且其具有的Ioc功能也实现了对动作的路由,也就是跨模块API调用!
可以理解为“通过URL找Class能实现的业务,ARouter都能实现”,这个概念要理解透彻!
我们在选择一款开源框架的时候,要综合考量开发团队质量、后期维护、功能需求等诸多方面,在综合对比之后ARouter脱颖而出
作为阿里开发团队的产品,ARouter在2016年年底就已经出现,并在2017年3月份的技术峰会上由阿里云资深开发工程师刘志龙公开分享,目前在GitHub具有2k+ star, ARouter的开发团队质量、后期维护都是相对比较稳定的
ARouter的优势:
- 使用注解,实现了映射关系自动注册 与 分布式路由管理
- 编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
- 映射关系按组分类、多级管理,按需初始化
- 灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常
- 自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
- 支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用 == 对动作的路由
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
- 支持获取Fragment
- 支持多模块使用,支持组件化开发
- 支持多种方式配置转场动画
- 支持MultiDex(Google方案)
ARouter的劣势:
- 不支持插件化 [已纳入开发计划]
- 不支持生成可读的映射关系文档 [已纳入开发计划]
- 不支持 多路径
- 不支持 手动分配路由
- 不支持 自定义路由匹配规则
三、ARouter的引入和使用
官方文档已经很清晰易懂,详见https://github.com/alibaba/ARouter
在这里简单列举下:
- 界面跳转类
ARouter.getInstance().build("/test/activity2").navigation();ARouter.getInstance().build("/test/activity2").navigation(this, requestCode);/*这种使用URi的方式中,URi的Scheme 和 host不影响结果,可以随便设,关键的是path* - build(URI)会把URI解析为path,并把当前URI存入PostCard* - build(String)构造的PostCard不存储URI*/Uri testUriMix = Uri.parse("xx://xxx/test/activity2");ARouter.getInstance().build(testUriMix) .withString("name", "老王") .withInt("age", 18) .withBoolean("boy", true) .withLong("high", 180) .withString("url", "https://a.b.c") .withParcelable("pac", testParcelable) .withObject("obj", testObj) .navigation();ARouter.getInstance().build("/test/activity2").navigation(Context mContext, int requestCode, NavigationCallback callback);
- Ioc依赖注入类
//【ByName方式】根据 注解的name对HelloService进行注入 ((HelloService) ARouter.getInstance().build("/service/hello").navigation()).sayHello("mike"); //【ByType方式】仅在HelloService接口只有一个实现时可用 = 根据classType实现注入 //当同一接口有多个实现的时候,必须使用byName的方式发现服务 ARouter.getInstance().navigation(HelloService.class).sayHello("mike"); //目标类直接注入 ARouter.getInstance().navigation(SingleService.class).sayHello("Mike");
- 降解策略类
//【1】实现DegradeService(实现了全局降级逻辑, IOC.byType方式被_ARouter调用)//【2】单次,使用NavigationCallback
- 其他
ARouter.openLog();ARouter.openDebug();//影响InstantRun + 打印路由表ARouter.init(getApplication());ARouter.getInstance().destroy();
四、源码分析
强烈要求:在阅读本文前详细阅读官方文档:开源最佳实践:Android平台页面路由框架ARouter,并了解以下概念:
- 编译器处理注解产生映射文件,运行期加载映射文件实现路由
- Bootstrapping、Extensibility以及Simple & Enough
- 编译期间:页面自动注册—注解&注解处理器
- 运行期间:动态加载—–分组管理,按需加载
- 全局拦截器
- 依赖注入
- 运行期动态修改路由
- 降级问题
本文约定:路由 = 路径URL : 目标类Class
- arouter-annotation: ARouter路由框架所使用的全部注解,及其相关类
- arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译器把注解标注的相关目标类生成映射文件
- arouter-api:实现路由控制
总的来说,就是arouter-annotation实现了路由表结构的定义,arouter-compiler在编译期完成了 构造路由表逻辑的创建, arouter-api在运行期加载逻辑构建路由表,并实现路由控制
4.1 arouter-annotation注解
【图4.1.1】
ARouter所提出的“分布式控制”就是通过不同目标Class的注解实现的,我们来看看ARouter都定义了那些注解:
4.1.1 @Route路由注解
@Route 是 ARouter 最重要的注解,也是路由最基本的节点,该注解主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。
值得说明的一点是 ARouter 并非仅提供页面(Activity)的路由功能,还可以用来路由模块想要暴露给其他模块调用的接口。
也就是说 @Route 不仅可用于 Activity 类,还可用于模块对外接口的实现类,实现类似于 AIDL 的功能,也就是IOC
@Target({ElementType.TYPE})@Retention(RetentionPolicy.CLASS)public @interface Route { //路径URL字符串 String path(); //组名,默认为一级路径名;一旦被设置,跳转时必须赋值 String group() default ""; //该路径的名称,用于产生JavaDoc String name() default "undefined"; //额外配置的开关信息;譬如某些页面是否需要网络校验、登录校验等 int extras() default Integer.MIN_VALUE; //该路径的优先级 int priority() default -1;}
4.1.2 @Interceptor拦截器注解
@Interceptor 是拦截器注解,拦截器是全应用全局的,不分module,只要集成进apk就起效
@Target({ElementType.TYPE})@Retention(RetentionPolicy.CLASS)public @interface Interceptor { //该拦截器的优先级 int priority(); //该拦截器的名称,用于产生JavaDoc String name() default "Default";}
4.1.3 @Autowired自动装载注解
@Autowired 是页面跳转时参数传递用的。目标Class中使用该注解标志的变量,会在页面被路由打开的时候,在调用Inject(“`)后自动赋予传递的参数值
@Target({ElementType.FIELD})@Retention(RetentionPolicy.CLASS)public @interface Autowired { // Mark param's name or service name. String name() default ""; // If required, app will be crash when value is null. // Primitive type wont be check! boolean required() default false; // Description of the field String desc() default "No desc.";}
4.1.4 RouteMeta路由元信息
如果全部路由信息认为是一张表格,那么RouteMeta就是表格的一行,代表路由表的一条元信息。
public class RouteMeta { private RouteType type; // 路由的类型: private Element rawType; // Raw type of route private Class<?> destination; // 目标Class private String path; // 路径URL private String group; // 分组 private int priority = -1; // 路由的优先级 private int extra; // 目标Class的额外配置开关信息;譬如某些页面是否需要网络校验、登录校验等 private Map<String, Integer> paramsType; // 目标Class的需要注入的参数 的参数名称:参数类型TypeKind}public enum RouteType {// 路由的类型 ACTIVITY(0, "android.app.Activity"), SERVICE(1, "android.app.Service"), PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"), CONTENT_PROVIDER(-1, "android.app.ContentProvider"), BOARDCAST(-1, ""), METHOD(-1, ""), FRAGMENT(-1, "android.app.Fragment"), UNKNOWN(-1, "Unknown route type"); int id; String className;} public enum TypeKind { //目标Class的需要注入的参数的参数类型 //基本类型 BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE, //封装类型 STRING, PARCELABLE, OBJECT; //使用Json解析}
4.2 arouter-compiler注解编译器
实现了“自动注册映射关系”也就是在编译期间自动生成映射文件,所以该module其实就是实现了一些注解处理器,目标在于生成映射文件与辅助文件(构造路由表逻辑的创建)
开源最佳实践:Android平台页面路由框架ARouter中“二、ARouter的技术方案”的“页面注册:注解&注解处理器”
1. 首先通过注解处理器扫出被标注的类文件;
2. 然后按照不同种类的源文件进行分类,这是因为ARouter是一个框架,其能够提供的功能非常多,所以不仅仅提供了跳转功能,它也能够实现模块之间的解耦,除此之外ARouter还能够提供很多的功能,像刚才提到的拦截器可以实现自动注册,其实ARouter中的所有组件都是自动注册的
3. 在按照不同种类的源文件进行分类完成之后,就能够按照固定的命名格式(工程名+$$+Group+$$+分组名)生成映射文件,这部分完成之后就意味着编译期的部分已经结束了
一般来说arouter-compiler所产生的class文件有以下几种:
项目目录下的 XXX$$工程名$$Autowired. 为自动装载的辅助类,核心为inject函数,实现对目标对象的成员变量的赋值
routes目录下
工程名$$Group$$分组名 【组内的路由清单列表】
(Map< String, RouteMeta> atlas
包含了对应分组下的,路由URL与目标对象Class的映射关系;
注意Router注解中无分组的话,默认以“/xx/xx”的第一个xx为分组名工程名$$Root$$$模块名 【组别的清单列表】
Map< String, Class< ? extends IRouteGroup>> routes
包含了组名与对应组内的路由清单列表Class的映射关系
是Arouter的“分组管理,按需加载”的实现。
ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量
那么什么时候加载分组结点呢?其实就是当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去,这就是ARouter的按需加载工程名$$Providers$$模块名 【Ioc的动作路由清单列表】
Map< String, RouteMeta> providers
PROVIDER 类型的路由节点的清单列表
包含了使用依赖注入方式的某class(实现了IProvide接口的直接子类)的 路由URL 与class映射关系
目标Class都实现了IProvider接口,借此实现部分路由转到该清单中
需要注意的是:Ioc动作路由清单其实只是 Route注解的一种特殊用法,总的来说,还是一种URL与目标类的映射关系
其实想一下,依赖注入,无非也就是指定好目标接口的目标类,然而实例化后进行赋值。URL就是指定说明工程名$$Interceptors$$模块名 【模块内的拦截器清单列表】
Map< Integer, Class< ? extends IInterceptor>> interceptors
包含了某个模块下的拦截器 与 优先级的映射关系
一个模块下的所有拦截器都在该类中包含,无分组特性,所以直接以模块名命名类文件
- 映射文件的生成并不是随意的,而是遵循一定的规则。是参照API Module的Temple的接口,进行创建的
- 可以认为像是一个人一样,他记录了API Module-Temple中接口的名称,函数的名称参数等信息,然后借用一定的工具,写出了继承这些函数的接口
- 注意:上述描述中,我们说的是“写出了接口”,就类似于在txt文档中写了一些文本,并不涉及引用,这也就意味着不需要依赖API Module,同时也标示API Module的Template中的模板不能随意更改
4.2.1 Route注解处理
区分路由类型是通过“是否为 Activity 、IProvider 、 Service 的子类”来判断的,详见“com.alibaba.android.arouter.compiler.processor.RouteProcessor”
在app中所定义的路由路径有:
@Route(path = "/test/activity1")public class Test1Activity extends AppCompatActivity {//```}@Route(path = "/test/activity2")public class Test2Activity extends AppCompatActivity {//```}@Route(path = "/test/activity3")public class Test3Activity extends AppCompatActivity {//```}@Route(path = "/test/activity4")public class Test4Activity extends AppCompatActivity {//```}/********************以下为Ioc的应用,都实现了IProvider接口**********************************/@Route(path = "/service/hello")public class HelloServiceImpl implements HelloService {//```}@Route(path = "/service/json")public class JsonServiceImpl implements SerializationService {//```}@Route(path = "/service/single")public class SingleService implements IProvider {//```}
我们先看下使用 annotationProcessor ‘com.alibaba:arouter-compiler:1.0.3’ 处理官方Demo的app所生成的映射文件:
【图4.2.1】
很明显,ARouter$$Group$$service和ARouter$$Group$$test分别是分组 service 和 test 的组内路由清单列表,我们可以把它想象成分别是 1班 和 2 班 的学生名单。
ARouter$$Root$$app 则是组别的清单列表,我们亦可以把它想成班级的清单,里面有两个班级,1班 和 2 班。
4.2.1.1 工程名$$Group$$分组名 【组内的路由清单列表】
public class ARouter\$\$Group\$\$service implements IRouteGroup { public ARouter\$\$Group\$\$service() { } public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", (Map)null, -1, -2147483648)); atlas.put("/service/json", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", (Map)null, -1, -2147483648)); atlas.put("/service/single", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", (Map)null, -1, -2147483648)); }}public class ARouter\$\$Group\$\$test implements IRouteGroup { public ARouter\$\$Group\$\$test() { } public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new HashMap() { { this.put("pac", Integer.valueOf(9)); this.put("obj", Integer.valueOf(10)); this.put("name", Integer.valueOf(8)); this.put("boy", Integer.valueOf(0)); this.put("age", Integer.valueOf(3)); this.put("url", Integer.valueOf(8)); } }, -1, -2147483648)); atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new HashMap() { { this.put("key1", Integer.valueOf(8)); } }, -1, -2147483648)); atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new HashMap() { { this.put("name", Integer.valueOf(8)); this.put("boy", Integer.valueOf(0)); this.put("age", Integer.valueOf(3)); } }, -1, -2147483648)); atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", (Map)null, -1, -2147483648)); atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", (Map)null, -1, -2147483648)); atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", (Map)null, -1, -2147483648)); }}
4.2.1.2 工程名$$Root$$$模块名 【组别的清单列表】
public class ARouter\$\$Root\$\$app implements IRouteRoot { public ARouter\$\$Root\$\$app() { } public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) { routes.put("service", service.class); routes.put("test", test.class); }}
4.2.1.3 工程名$$Providers$$模块名 【Ioc的动作路由清单列表】
特别的,请注意:PROVIDER 类型的路由节点既存在于存在于对应的分组中,也存在于该类型的清单列表中
这就好像学生中有一些是少先队员,每个班级中都有可能有少先队员,而学校又有一份少先队员的总列表。这就意味着我们有两种方式来查找到一个可确定的少先队员。
所以,ARouter 可通过两种方式来获取 PROVIDER 类型的路由节点
- ByName的方式:通过【组内的路由清单列表】
根据 注解的name对HelloService进行注入
((HelloService) ARouter.getInstance().build(“/service/hello”).navigation()).sayHello(“mike”); - ByType的方式 :通过【Ioc的动作路由清单列表】
仅在HelloService接口只有一个实现时可用 = 根据classType实现注入
当同一接口有多个实现的时候,必须使用byName的方式发现服务
ARouter.getInstance().navigation(HelloService.class).sayHello(“mike”);
注意名称是 (实现了IProvide接口的直接子类)
- HelloServiceImpl的名称是 其父类HelloService,HelloService实现了IProvide接口
- JsonServiceImpl的名称是 其父类SerializationService,SerializationService实现了IProvide接口
- SingleService的名称是 自身SingleService,SingleService实现了IProvide接口
public class ARouter\$\$Providers\$\$app implements IProviderGroup { public ARouter\$\$Providers\$\$app() { } public void loadInto(Map<String, RouteMeta> providers) { providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", (Map)null, -1, -2147483648)); providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", (Map)null, -1, -2147483648)); providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", (Map)null, -1, -2147483648)); }}
4.2.2 Autowired注解处理
图4.2.1并没有显示自动装载的辅助类,是由于test-module-1中没有使用 Autowired 注解,最后我们再给出下app的映射文件结构
【图4.2.2】
@Route(path = "/test/activity1")public class Test1Activity extends AppCompatActivity { @Autowired String name; @Autowired int age; @Autowired(name = "boy") boolean girl; @Autowired TestParcelable pac; @Autowired TestObj obj; private long high; @Autowired String url; @Autowired HelloService helloService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test1); ARouter.getInstance().inject(this); // No more getter ``` // name = getIntent().getStringExtra("name"); // age = getIntent().getIntExtra("age", 0); // girl = getIntent().getBooleanExtra("girl", false); // high = getIntent().getLongExtra("high", 0); // url = getIntent().getStringExtra("url"); String params = String.format( "name=%s,\n age=%s,\n girl=%s,\n high=%s,\n url=%s,\n pac=%s,\n obj=%s", name, age, girl, high, url, pac, obj ); helloService.sayHello("Hello moto."); ((TextView)findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName()); ((TextView)findViewById(R.id.test2)).setText(params); }}public class Test1Activity\$\$ARouter\$\$Autowired implements ISyringe { private SerializationService serializationService; public Test1Activity\$\$ARouter\$\$Autowired() { } public void inject(Object target) { this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class); Test1Activity substitute = (Test1Activity)target; substitute.name = substitute.getIntent().getStringExtra("name"); substitute.age = substitute.getIntent().getIntExtra("age", 0); substitute.girl = substitute.getIntent().getBooleanExtra("boy", false); substitute.pac = (TestParcelable)substitute.getIntent().getParcelableExtra("pac"); if(null != this.serializationService) { substitute.obj = (TestObj)this.serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class); } else { Log.e("ARouter::", "You want automatic inject the field \'obj\' in class \'Test1Activity\' , then you should implement \'SerializationService\' to support object auto inject!"); } substitute.url = substitute.getIntent().getStringExtra("url"); substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class); }}public class BlankFragment\$\$ARouter\$\$Autowired implements ISyringe { private SerializationService serializationService; public BlankFragment\$\$ARouter\$\$Autowired() { } public void inject(Object target) { this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class); BlankFragment substitute = (BlankFragment)target; substitute.name = substitute.getArguments().getString("name"); if(null != this.serializationService) { substitute.obj = (TestObj)this.serializationService.json2Object(substitute.getArguments().getString("obj"), TestObj.class); } else { Log.e("ARouter::", "You want automatic inject the field \'obj\' in class \'BlankFragment\' , then you should implement \'SerializationService\' to support object auto inject!"); } if(null == substitute.obj) { Log.e("ARouter::", "The field \'obj\' is null, in class \'" + BlankFragment.class.getName() + "!"); } }}
我们分析下Test1Activity$$ARouter$$Autowired的代码,核心就是inject(Object target)
- 通过Arouter路由框架的IOc的ByType方式对SerializationService进行注入,该类为Json转换的工具类
- 注意助理使用的是ByType方式,也就是直接找到实现了SerializationService接口的唯一类进行实例化并注入,如果实现了SerializationService接口的有多个类,那么就会出现问题
- 所以全局应用的所有模块中,只能存在一个实现了SerializationService接口的类
- 获取目标对象实例
- 利用目标对象的对应传值方式,对目标对象的实例中的成员变量进行赋值
- Acitivty使用的getIntent()—–由框架自身的参数传递决定,详见 4.3API部分
- Fragment使用getArguments()—–由框架自身的参数传递决定,详见 4.3API部分
- OBJ对象使用JSon辅助类进行实例化转换—– 详见 4.3API部分,传递参数时会将对象封装为json字符串
- IOc依赖注入对象,默认使用byType方式,如果Autowired注解中有标识name,则使用name指向的类实例并赋值
4.2.3 Interceptor注解处理
@Interceptor(priority = 7)public class Test1Interceptor implements IInterceptor {//```}public class ARouter\$\$Interceptors\$\$app implements IInterceptorGroup { public ARouter\$\$Interceptors\$\$app() { } public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) { interceptors.put(Integer.valueOf(7), Test1Interceptor.class); }}
4.3 arouter-api路由控制
【图4.3.1 图4.3.2】
- 最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
- 在Launcher层的下一层就是Frossard层,从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template.
- 这里的Service概念和服务端的Service概念是相似的,也是在客户端的简单引申,但是却不同于Android组件中的Service,这里的Service是ARouter抽象出来的概念,从本质上讲,这里的Service是接口,从意义上讲是将一定的功能和组件封装成接口,并对外提供能力。
- Template则是模板。Compiler模块在编译期会生成一些映射文件,而这些映射文件的生成规则就是根据Template来生成的,通过记录Template的相关接口函数名称+参数等,生成加载逻辑的代码,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。
- 再往下一层就完全是SDK的内部实现了,这一层包括了Ware House、Thread、Log、Exception以及Class工具。
- Ware House主要存储了ARouter在运行期间加载的一些配置文件以及映射关系;
- 而Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的;
- Class工具则是用于解决不同类型APK的兼容问题的。
- 再下一层就是Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层,当然也会按照功能模块进行划分。
4.3.1 ARouter init 初始化过程分析
经过前面的分析,我们几乎可以断定,ARouter 的初始化过程最重要的一步一定是把前面编译产生的路由清单文件加载到内存,形成一个路由表,以供后面路由查找之用。
Class ARouter 其实是一个代理类,它的所有函数实现都交给Class _ARouter去实现,两个都是单例模式
我们先来看 arouter-api 中的 com.alibaba.android.arouter.launcher.ARouter
/*** Init, it must be call before used router.*/public static void init(Application application) {//静态函数进行初始化,不依赖对象 if (!hasInit) { logger = _ARouter.logger; //持有 日志打印的 全局静态标量 _ARouter.logger.info(Consts.TAG, "ARouter init start.");//打印 ARouter初始化日志 hasInit = _ARouter.init(application);//移交 _ARouter去 初始化 if (hasInit) { _ARouter.afterInit(); } _ARouter.logger.info(Consts.TAG, "ARouter init over.");//打印 ARouter初始化日志 }}
既然代码移交到了 _ARouter去 初始化,我们再来看
protected static synchronized boolean init(Application application) { mContext = application;// Application的上下文 LogisticsCenter.init(mContext, executor);//移交逻辑中心进行初始化,并传入线城池对象 logger.info(Consts.TAG, "ARouter init success!");//打印日志 hasInit = true;//标示是否初始化完成 // It's not a good idea. // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback()); // } return true; }
继续往下走, LogisticsCenter 中进行初始化
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { mContext = context; //静态持有Application的上下文 executor = tpe;//静态持有 线城池 try { // These class was generate by arouter-compiler. // 通过指定包名com.alibaba.android.arouter.routes,找到所有 编译期产生的routes目录下的类名(不包含装载类) List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE); for (String className : classFileNames) {//【组别的清单列表】com.alibaba.android.arouter.routes.ARouter\$\$Root if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { // This one of root elements, load root. ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {//【模块内的拦截器清单列表】com.alibaba.android.arouter.routes.ARouter\$\$Interceptors // Load interceptorMeta ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {//【Ioc的动作路由清单列表】com.alibaba.android.arouter.routes.ARouter\$\$Providers // Load providerIndex ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } } if (Warehouse.groupsIndex.size() == 0) { logger.error(TAG, "No mapping files were found, check your configuration please!"); } if (ARouter.debuggable()) { logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size())); } } catch (Exception e) { throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]"); } }
首先,我们要有一个概念。前文说了,我们断定初始化操作是要把 映射信息加载到内存,那么在哪里存储呢?这里就引入了 内存仓库Warehouse的概念,实际上它就是持有了多个statice 的Map对象,在下文我们给出其源码。
我们继续分析LogisticsCenter.init(),首先就是根据包名com.alibaba.android.arouter.routes找到了下边的所有类,那些类呢?所有模块下的这几个类:
【图4.3.1.1】
这些类都是编译期间通过“手动”的方式创建出来的,他们分别继承了IRouteRoot、IInterceptorGroup、IProviderGroup接口,实现了其loadInto()方法
接下来就是,根据反射将类实例化出来后,调用其loadInto()将对应的路由信息加载到 内存仓库中
需要注意的是:初始化阶段只加载了Root【组别的清单列表】,,并没有具体载入每个 Group 中包含的具体的路由节点清单,这就与 ARouter 的官方说明一致了:映射关系按组分类、多级管理,按需初始化,只有我们使用到具体的 Group 时,才会加载对应的 Group 列表。
class Warehouse { // Cache route and metas static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();//【组别的清单列表】 包含了组名与对应组内的路由清单列表Class的映射关系(这里只存储了未导入到 routes在键盘每个的组) static Map<String, RouteMeta> routes = new HashMap<>();//【组内的路由清单列表】包含了对应分组下的,路由URL与目标对象Class的映射关系; // Cache provider static Map<Class, IProvider> providers = new HashMap<>(); //缓存 IOC 目标class与已经创建了的对象 TODO ?全局应用共享一个IOc依赖注入对象? static Map<String, RouteMeta> providersIndex = new HashMap<>();//【Ioc的动作路由清单列表】包含了使用依赖注入方式的某class的 路由URL 与class映射关系 // Cache interceptor //【模块内的拦截器清单列表】包含了某个模块下的拦截器 与 优先级的映射关系 static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]"); static List<IInterceptor> interceptors = new ArrayList<>();//已排序的拦截器实例对象 //```}
最后,别忘了最开始的_ARouter.afterInit(),根据 Ioc.ByName()方式获取 拦截器界面,注意这个拦截器并不是我们定义的拦截器,而是Arouter实现的拦截器逻辑,它持有我们定义的拦截器,可以理解为“拦截器截面控制器”
static void afterInit() { // Trigger interceptor init, use byName. interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation(); }
至此,初始化工作全部完成,其中 内存仓库Warehouse缓存了全局应用的【组别的清单列表】、【Ioc的动作路由清单列表】、【模块内的拦截器清单列表】,3个以_index为结尾的Map对象
4.3.2 ARouter 运行时 API 调用过程分析
入口:
ARouter.getInstance() .build("/test/activity2") .navigation();
单例模式我们不过多赘述,我们看build的最终调用,其使用了代理类_ARouter的build()并构建和返回PostCard对象,先简单的提一下 一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程
public Postcard ARouterbuild(String path) { return _ARouter.getInstance().build(path); } protected Postcard _ARouter.build(String path) { if (TextUtils.isEmpty(path)) {//如果路径为null throw new HandlerException(Consts.TAG + "Parameter is invalid!"); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);//通过ARouter的Ioc方式(IProvider的ByType())方式找到 动态修改路由类 if (null != pService) { path = pService.forString(path); //如果全局应用有实现 PathReplaceService.class接口,则执行 “运行期动态修改路由”逻辑。生成转换后的路由 } return build(path, extractGroup(path)); } }
其实整个build的过程分为两个顺序部分:
- 使用 Ioc byType()方式寻找PathReplaceService.class接口的实现类,以期实现 “运行期动态修改路”
- 正常的本次路由导航
4.3.2.1 运行期动态修改路由PathReplaceService的实现(IOC 也就是IProvider.byType() = navigation(class)的实现方式,用于获取 路由目标实例)
在_ARouter的build()中又调用了ARouter的T navigation(Class
protected <T> T _ARouter.navigation(Class<? extends T> service) { try { Postcard postcard = LogisticsCenter.buildProvider(service.getName()); // Compatible 1.0.5 compiler sdk. if (null == postcard) { // No service, or this service in old version. postcard = LogisticsCenter.buildProvider(service.getSimpleName()); } LogisticsCenter.completion(postcard); return (T) postcard.getProvider(); } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage()); return null; } } //1. 从内存仓库的【Ioc的动作路由清单列表】中找到,对应Name对应的 路由元信息 //2. 根据路由元信息 生成 Postcard对象,赋值其 路径URL 和 组名 信息 public static Postcard LogisticsCenter.buildProvider(String serviceName) { RouteMeta meta = Warehouse.providersIndex.get(serviceName); if (null == meta) { return null; } else { return new Postcard(meta.getPath(), meta.getGroup()); } }
找到Postcard后,执行 LogisticsCenter.completion(postcard)函数,这个函数是一个至关重要的函数,其在于完善postcard对象,而后调用其getProvider()获取目标对象实例。具体的细节我们在4.2.2.2中讲解
4.3.2.2 一次简单的路由导航
入口:_ARouter.build(path, extractGroup(path)), 其中 _ARouter.extractGroup(string)从路径中找到,默认的 分组,也就是 一级路径,例如”/11/22”—11
可以看到,该函数也执行了一次 “4.3.2.1 运行期动态修改路由”逻辑,这是由于,该函数可以被单独调用所必须要求的
//根据路径名 和 分组名构建 PostCard对象 protected Postcard _ARouter.build(String path, String group) { if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!"); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } return new Postcard(path, group); } }
在得到Postcard对象后,调用其navigation()函数,我们再看看PostCard类中的该函数
public Object navigation() { return navigation(null); } public Object navigation(Context context) { return navigation(context, null); } public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); }
可见最终,还是调用ARouter中的navigation方法:
public Object ARouter.navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) { return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback); } //一次路由跳转的最终调用函数,包含 查找回调的调用、拦截器处理、绿色通道校验、和具体路由操作 protected Object _ARouter.navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { try { LogisticsCenter.completion(postcard);//【1】完善postcard。当前只有 path和group } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage()); if (debuggable()) { // Show friendly tips for user. Toast.makeText(mContext, "There's no route matched!\n" + " Path = [" + postcard.getPath() + "]\n" + " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show(); } if (null != callback) { callback.onLost(postcard);//【2】执行到这里,触发查找失败 } else { // No callback for this invoke, then we use the global degrade service. DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);//【2】执行到这里,使用IOc.byType()的方式 全局降级策略的实现 if (null != degradeService) { degradeService.onLost(context, postcard); } } return null; } if (null != callback) {//【2】执行到这里,说明找到了路由元信息,触发 路由查找的回调 callback.onFound(postcard); } if (!postcard.isGreenChannel()) {//【3】绿色通道校验 // It must be run in async thread, maybe interceptor cost too mush time made ANR. interceptorService.doInterceptions(postcard, new InterceptorCallback() {//调用拦截器截面控制器,遍历内存仓库的自定义拦截器,并在异步线程中执行拦截函数 /** * Continue process * * @param postcard route meta */ @Override public void onContinue(Postcard postcard) { _navigation(context, postcard, requestCode, callback);//【4】根据 路由类型执行具体路由操作 } /** * Interrupt process, pipeline will be destory when this method called. * * @param exception Reson of interrupt. */ @Override public void onInterrupt(Throwable exception) { if (null != callback) { callback.onInterrupt(postcard); } logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage()); } }); } else { return _navigation(context, postcard, requestCode, callback);//【4】根据 路由类型执行具体路由操作 } return null; }
具体的路由操作是交由 _ARouter._navigation(“`)执行的
//根据 路由类型执行具体路由操作 private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = null == context ? mContext : context; switch (postcard.getType()) { case ACTIVITY: //【1】如果是Acitvity,则实现Intent跳转 // Build intent final Intent intent = new Intent(currentContext, postcard.getDestination()); intent.putExtras(postcard.getExtras()); // Set flags. int flags = postcard.getFlags(); if (-1 != flags) { intent.setFlags(flags); } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Navigation in main looper. new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { if (requestCode > 0) { // Need start for result ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle()); } else { ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle()); } if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version. ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim()); } if (null != callback) { // Navigation over. callback.onArrival(postcard); } } }); break; case PROVIDER: //【2】如果是Ioc,则返回目标对象实例 return postcard.getProvider(); case BOARDCAST: //【4】如果是board,则返回实例 case CONTENT_PROVIDER: //【5】如果是Cp,则返回实例 case FRAGMENT://【6】如果是Fragment,则返回实例,并填充bundle Class fragmentMeta = postcard.getDestination(); try { Object instance = fragmentMeta.getConstructor().newInstance(); if (instance instanceof Fragment) { ((Fragment) instance).setArguments(postcard.getExtras()); } else if (instance instanceof android.support.v4.app.Fragment) { ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras()); } return instance; } catch (Exception ex) { logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace())); } case METHOD:// case SERVICE:// default: return null; } return null; }
好了,至此我们就完成了基本的跳转
4.3.2.3 PostCard对象及LogisticsCenter.completion()
一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程
public final class Postcard extends RouteMeta { // Base private Uri uri; //Nullable,仅当根据Uri方式跳转时有值 private Object tag; // 拦截器中出现异常的tag private Bundle mBundle; // Intent中存储的Bundle以传递信息 private int flags = -1; // Intenet中的flag,譬如Clear_TOP private int timeout = 300; // TimeUnit.Second 拦截器的耗时阈值 private IProvider provider; // Nullable 当使用Ioc的时候,结果会导致该变量被赋值为目标对象实例 private boolean greenChannel; // 是否跳过拦截器 private SerializationService serializationService;//用于对象的json转换的工具,在构建参数withObject是会被ByType方式赋值进来 // Animation private Bundle optionsCompat; // The transition animation of activity private int enterAnim; private int exitAnim;}
我们来看看 完善方法 completion(Postcard postcard)
public synchronized static void completion(Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!"); } RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());//根据路径URL获取到路径元信息 if (null == routeMeta) { // Maybe its does't exist, or didn't load. 如果未获取到路径元信息,可能是由于 未加载对应分组的【组内清单列表】 or 的确没有 Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.从【组别的清单列表】拿到对应组的 组内清单创建逻辑 if (null == groupMeta) {//如果为空,则丢出异常,未找到 throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]"); } else { // Load route and cache it into memory, then delete from metas. try { if (ARouter.debuggable()) { logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath())); } IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();//实例化【组内清单创建逻辑】 iGroupInstance.loadInto(Warehouse.routes);//将该组的【组内清单列表】加入到内存仓库中 Warehouse.groupsIndex.remove(postcard.getGroup());//从【组别的清单列表】移除当前组 if (ARouter.debuggable()) { logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath())); } } catch (Exception e) { throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]"); } completion(postcard); // Reload 再次触发完善逻辑 } } else { postcard.setDestination(routeMeta.getDestination());//【3】目标 class postcard.setType(routeMeta.getType());//【4】路由类型 postcard.setPriority(routeMeta.getPriority());//【5】路由优先级 postcard.setExtra(routeMeta.getExtra());//【6】额外的配置开关信息 Uri rawUri = postcard.getUri(); if (null != rawUri) { // Try to set params into bundle. 【7】如果有URI,则根据路由元信息的“目标Class的需要注入的参数 的参数名称:参数类型TypeKind paramsType”和“URI的?参数”进行赋值 Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);//“URI的?参数” Map<String, Integer> paramsType = routeMeta.getParamsType();//“目标Class的需要注入的参数 的参数名称:参数类型TypeKind ” if (MapUtils.isNotEmpty(paramsType)) { // Set value by its type, just for params which annotation by @Param for (Map.Entry<String, Integer> params : paramsType.entrySet()) {//目标Class的需要注入的参数 的参数名称:参数类型TypeKind”,向postcard的bundle中put对应的携带数据 setValue(postcard, params.getValue(),//参数类型TypeKind params.getKey(),//参数名称 resultMap.get(params.getKey()));//参数值 } // Save params name which need autoinject. 在bundle存入字符数组 wmHzgD4lOj5o4241:待注入参数列表 postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{})); } // Save raw uri 在bundle存入字符串 NTeRQWvye18AkPd6G:Uri postcard.withString(ARouter.RAW_URI, rawUri.toString()); } switch (routeMeta.getType()) { case PROVIDER: // if the route is provider, should find its instance 【8】PROVIDER类型的路由则实现 实例化目标类 + 绿色通道(byType方式的核心实现) // Its provider, so it must be implememt IProvider Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();//目标 class IProvider instance = Warehouse.providers.get(providerMeta);//目标class的实例对象是否已经存在了 if (null == instance) { // There's no instance of this provider 如果存在了就直接使用,不存在才创建新的? TODO Ioc导致全局共享一个对象? IProvider provider; try { provider = providerMeta.getConstructor().newInstance(); provider.init(mContext); Warehouse.providers.put(providerMeta, provider); instance = provider; } catch (Exception e) { throw new HandlerException("Init provider failed! " + e.getMessage()); } } postcard.setProvider(instance);//【8.1 实例化并持有目标类】 postcard.greenChannel(); //【8.2 绿色通道】 Provider should skip all of interceptors break; case FRAGMENT: postcard.greenChannel(); // Fragment needn't interceptors 【9】如果是Fragment则设置绿色通道 default: break; } } }
4.3.2.4 facade分析
图
CallBack
- InterceptorCallback 拦截器的callbck
- NavigationCallback 降级逻辑的全部回调
NavCallback 降级逻辑的部分回调
Service
- AutowiredService 实现了“加载并调用辅助类以实现自动装载”, IOC.byName(/arouter/service/autowired)方式被_ARouter调用
- ClassLoaderService 未使用
- DegradeService 实现了全局降级逻辑, IOC.byType方式被_ARouter调用
- InterceptorService实现了拦截器截面逻辑, IOC.byName(/arouter/service/interceptor)方式被_ARouter调用
- PathReplaceService 实现了动态改变路由逻辑, IOC.byType方式被_ARouter调用
- SerializationService 全局对Object对象的json转换工具类,IOC.byType方式被PostCard调用
五、其他
5.1 Arouter所提供的IOC的单例特性
内存仓库Warehouse.providers持有了 class与对象实例的映射关系,这就导致Ioc依赖注入的时候,全局应用共享一个实例对象
【图5.1.1】
据此,我们也咨询了Arouter的设计人员
【图5.1.2】
iprovider理解为 主要针对“对外提供api的服务”
5.2 关于Compiler中源码的解读
- (2.1.19.3)深入理解Java:注解(Annotation)–编译时注解的处理
阅读上面的文章,基本就全部了解了
- (4.2.40)阿里开源路由框架ARouter的源码分析
- 阿里路由框架--ARouter 源码解析之初始化ARouter
- 浅谈Android模块化设计(路由框架ARouter源码分析)
- 阿里ARouter源码分析
- 阿里路由框架--ARouter 源码解析之Compiler
- 【PDF下载】阿里开源峰会之安卓页面路由框架ARouter最佳实践
- ARouter 拦截器与路由的实现(源码分析)
- 开源最佳实践:Android平台页面路由框架ARouter
- ARouter路由框架介绍
- ARouter 路由框架
- Android组件化方案—阿里路由ARouter的使用
- ARouter Android页面路由框架
- ARouter--简单且好用的Android路由框架
- [Alibaba-ARouter] 简单好用的Android页面路由框架
- [Alibaba-ARouter] 简单好用的Android页面路由框架
- Android平台阿里页面路由ARouter
- ARouter源码解析02-加载路由表单
- ARouter源码解析03-路由跳转
- springboot整合tomcat自带的websocket实现在线聊天及象棋网页对战功能
- mysql 的诡异现象,CPU 100%,order by 主键提示 sending data
- selenium学习之子页面(iframe)切换以及自动化速度较快的问题
- 欢迎使用CSDN-markdown编辑器
- lintcode/leetcode由易至难第17题:Counting Bits
- (4.2.40)阿里开源路由框架ARouter的源码分析
- 关于IP地址定位、IP查询和IP地址库 你想了解的历史都在这里
- tp3.2.3模板替换__MODULE__
- @Field parameters can only be used with form encoding
- MySQL函数
- Android 数据存储
- chm转换格式
- 【mongoDB中级篇②】索引与expain
- result结果类型