Android开发组件化的一些思考
https://www.jianshu.com/p/0f03602d4759
ARouter LiveDataBus
publicImplementation = [
]
other.each {
implementation project(it)
}
sourceSets {
ext.android.isApplication
manifest.srcFile ''
}
组件化跳转分析
ARouter
然后在编译时生成类文件及类方法,该类方法内利用Map收集对应的注解了的类,在Application创建时,执行这些类文件相关方法,收集到ARouter容器内。
Android APT(Annotation Processing Tool) 实践
https://juejin.im/post/6844903743918456846
Android APT Annotation Processing Tool
Target Retention RetentionPolicy.CLASS
AbstractProcessor process
TypeElement set
Processor
SupportedAnnotationTypes
MethodSpec TypeSpec JavaFile
android-apt
compile project annotation
apt project compiler
apt实战
在annotation lib 中创建两个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BYView {
int value() default 0;
}
annotation lib
Set< Element>
DIActivity annotation = element.getAnnotation(DIActivity.class);//获取注解的信息
element.getAnnotation DIActivity.class
用方法
常用Element子类
TypeElement:类
ExecutableElement:成员方法
VariableElement:成员变量
通过包名和类名获取TypeName
TypeName targetClassName = ClassName.get(“PackageName”, “ClassName”);
通过Element获取TypeName
TypeName type = TypeName.get(element.asType());
获取TypeElement的包名
String packageName = processingEnv.getElementUtils().getPackageOf(type).getQualifiedName().toString();
获取TypeElement的所有成员变量和成员方法
List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(typeElement);
TypeElement
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(DIActivity.class);
string, ? extends IRouteGroup > routes;
TypeName.get element.asType
谈谈APT和JavaPoet的一些使用技巧和要点
https://xsfelvis.github.io/2018/06/06/%E8%B0%88%E8%B0%88APT%E5%92%8CJavaPoet%E7%9A%84%E4%B8%80%E4%BA%9B%E6%8A%80%E5%B7%A7/
ARouter ButterKnife、ARouter等,在编译时根据annotation生成相关的代码逻辑,动态的生成Java
Dagger2、ButterKnife、ARouter等,在编译时根据annotation生成相关的代码逻辑,动态的生成Java
PermissionProxy<T>
Map<String,Class<? extends IRouteGroup>> groupsIndex = new HashMap
RouteMeta routes
IProvider RouteMeta IInterceptor
ParameterizedType
getConstructor.newInstance
ParameterizedTypeName.get
ParameterSpec.build
final ClassName java_lang_Class = ClassName.get(Class.class);
final ClassName java_util_Collections = ClassName.get("java.util", "Collections");
final ClassName java_util_Map = ClassName.get("java.util", "Map");
final ClassName java_util_Set = ClassName.get("java.util", "Set");
final ClassName java_util_LinkedHashMap = ClassName.get("java.util", "LinkedHashMap");
final ClassName java_util_LinkedHashSet = ClassName.get("java.util", "LinkedHashSet");
final ClassName instantiator = ClassName.get("java.util.concurrent", "Callable");
final TypeName classOfAny = ParameterizedTypeName.get(java_lang_Class, any);
final TypeName instantiatorOfAny = ParameterizedTypeName.get(instantiator, any);
final TypeName instantiatorOfP = ParameterizedTypeName.get(instantiator, p);
final TypeName classOfS = ParameterizedTypeName.get(java_lang_Class, s);
final TypeName classOfP = ParameterizedTypeName.get(java_lang_Class, p);
final TypeName classOfSubTypeOfS = ParameterizedTypeName.get(java_lang_Class, subTypeOfS);
final TypeName setOfClass = ParameterizedTypeName.get(java_util_Set, classOfAny);
final TypeName setOfClassOfSubTypeOfS = ParameterizedTypeName.get(java_util_Set, classOfSubTypeOfS);
final TypeName linkedHashSetOfClass = ParameterizedTypeName.get(java_util_LinkedHashSet, classOfAny);
final TypeName mapOfClassToSetOfClass = ParameterizedTypeName.get(java_util_Map, classOfAny, setOfClass);
final TypeName mapOfClassToInstantiator = ParameterizedTypeName.get(java_util_Map, classOfAny, instantiatorOfAny);
final TypeName linkedHashMapOfClassToSetOfClass = ParameterizedTypeName.get(java_util_LinkedHashMap, classOfAny, setOfClass);
final TypeName linkedHashMapOfClassToInstantializer = ParameterizedTypeName.get(java_util_LinkedHashMap, classOfAny, instantiatorOfAny);
FieldSpec.builder
package com.example; // PackageElement
public class Test { // TypeElement
private int a; // VariableElement
private Test other; // VariableElement
public Test () {} // ExecuteableElement
public void setA ( // ExecuteableElement
int newA // TypeElement
) {}
}
TypeParameterElement
TypeElement elementUtils.getPackageCodePath
ElementType
AnnotationProcessor
注解
注解基础
注解分类
谈谈APT和JavaPoet的一些使用技巧和要点
06-06
编译期注解处理之APT
04-07
JavaPoet
03-12
注解基础
https://xsfelvis.github.io/categories/%E6%B3%A8%E8%A7%A3/
注解基础
https://xsfelvis.github.io/2017/01/06/%E6%B3%A8%E8%A7%A3%E5%9F%BA%E7%A1%80/
APT JavaPoet
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityPath {
String value();
}
Target(ElementType.TYPE)
Retention RetentionPolicy.RUNTIME
IRouteGroup RouteMeta
IProvider RouteMeta IProvider
IInterceptor interceptors
Cache interceptor
Cache provider
annotation_compiler模块)
AutoService
TypeParameterElement
SupportedAnnotationTypes
@SupportedAnnotationTypes({Constant.ACTIVITY_PATH}) 指定注解,这里填写ActivityPath的类的全限定名称 包名.ActivityPath
AutoService(Processor.class)
//加载apk存储路径给DexFile
DexFile df = new DexFile(mContext.getPackageCodePath());
Enumeration<String> enumeration = df.entries();
DexFile df.entries
ARouter源码解析
https://blog.csdn.net/youyu_torch/article/details/95251221
Arouter解析.getInstance.build
.withLong.navigation
ARouter概述
ARouter使用
ARouter源码分析
arouter-annotation注解
arouter-compiler注解编译器
arouter-api路由控制
ARouter 初始化
ARouter API跳转
拦截器原理
annotation注解 compiler 注解编译器
arouter-annotation: 定义路由表的结构,ARouter路由框架所使用的全部注解,及其相关类
arouter-compiler: 创建路由表,注解编译处理器,引入“arouter-annotation”,在编译期把注解标注的相关目标类生成映射文件
arouter-api: 在运行期加载逻辑构建路由表,并实现路由控制
基于此,ARouter提供了两个SDK:一个是arouter-api,面向运行期;另一个是arouter-compiler,面向编译期。
arouter-api arouter-compile
aRouter-annotation
arouter-compiler使用注解自动注册,生成映射文件后,arouter-api将通过读取信息,生成路由表元信息,从而建立映射关系
compiler最主要的还是processor文件下的三个类,用于在编译器读取注解,生成映射文件
首先通过注解处理器扫出被标注的类文件;
然后按照不同种类的源文件进行分类,这是因为ARouter是一个框架,其能够提供的功能非常多,所以不仅仅提供了跳转功能,它也能够实现模块之间的解耦,除此之外ARouter还能够提供很多的功能,像刚才提到的拦截器可以实现自动注册,其实ARouter中的所有组件都是自动注册的
在按照不同种类的源文件进行分类完成之后,就能够按照固定的命名格式(工程名Group分组名)生成映射文件,这部分完成之后就意味着编译期的部分已经结束了
for (String className : routerMap) {
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)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
IRouteRoot Class.forName.getConstructor.newInstance.loadInto Warehouse.groupIndex
ARouter$$Root$$app
loadInto String,Class IRouteGroup routes
routes.put("service",service.class)
routes.put("test")
ByName ByType 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”);
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));
}
}
Arouter$$Providers\$\$app implements IProviderGroup
providers.put("HelloService"),
RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", (Map)null, -1, -2147483648));
RouteMeta.build RouteType.PROVIDER,HelloServiceImpl.class,"/service/hello","service",
Autowired注解处理
图4.2.1并没有显示自动装载的辅助类,是由于test-module-1中没有使用 Autowired 注解,最后我们再给出下app的映射文件结构
【图4.2.2】
Autowired注解处理
public class BlankFragment\$\$ARouter\$\$Autowired implements ISyringe {
private SerializationService serializationService;
public BlankFragment\$\$ARouter\$\$Autowired() {
}
substitute.getArgu
Annotation型定义为@interface
Field[] fields = clazz.getDeclaredFields(); //获取自身的不包括继承类
run_methodInfo methodInfo = method.getAnnotation(run_methodInfo.class);
getDeclaredFields
method.getAnnotation run_methodInfo.class
JavaPoet
https://xsfelvis.github.io/2017/03/12/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BJavaPoet/
常用类
使用javapoet前需要了解4个常用类
MethodSpec 代表一个构造函数或方法声明。
TypeSpec 代表一个类,接口,或者枚举声明。
FieldSpec 代表一个成员变量,一个字段声明。
JavaFile包含一个顶级类的Java文件。
国际惯例先自动生成一个helloWorld类
TypeSpec
RetentionPolicy.Class
addStatement
javapoet
$L
ClassName ParameterizedTypeName
TypeSpec hello = classBuilder
.addMethod
.build
addStaticImport Collections.class
$N
addStatement
addParameter
Type.anonymousInnerClass $L
编译期注解处理之APT
https://xsfelvis.github.io/2017/04/07/%E7%BC%96%E8%AF%91%E6%9C%9F%E6%B3%A8%E8%A7%A3%E4%B9%8BAPT/
Compile time 编译期注解之APT/
@Retention(RetentionPolicy.CLASS)
AbstractProcessor的子类,并且调用该类型的process函数
AbstractProcessor process
ProcessingEnvironment
Elements Types Filer
APT中的Elements和TypeMirrors
APT中的Elements和TypeMirrors
在前面的init()中我们可以获取如下引用
Elements:一个用来处理Element的工具类
Types:一个用来处理TypeMirror的工具类
Filer:正如这个名字所示,使用Filer你可以创建文件(通常与javapoet结合)
ExecutableElement
RetentionPolicy.Class
getAnnotationMirrors
阿里开源路由框架ARouter的源码分析
https://juejin.im/entry/6844903505937842183
ARouter
映射关系按组分类、多级管理,按需初始化
自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
PostCard对象
build
Ioc依赖注入类
//【ByName方式】根据 注解的name对HelloService进行注入
((HelloService) ARouter.getInstance().build("/service/hello").navigation()).sayHello("mike");Ioc依赖注入类
//【ByName方式】根据 注解的name对HelloService进行注入
((HelloService) ARouter.getInstance().build("/service/hello").navigation()).sayHello("mike");
开源最佳实践:Android平台页面路由框架ARouter
https://developer.aliyun.com/article/71687
优势一:直接解析URL路由,解析参数并赋值到对应目标字段的页面中。
优势二:支持多模块项目,因为现在很少有APP是单模块的项目,一般都是多模块单工程的,由不同的团队负责不同的模块开发,这时候支持多模块项目开发就显得尤为重要。
优势三:支持InstantRun,目前很多路由框架并不支持InstantRun,而InstantRun是Google在AndroidStudio2.0阿尔法版本中提供的新功能,其类似于代码的日更新,其只不过面向的是开发过程,这样做可以在开发的过程中减少开发和编译的次数,可以简单地将代码修改即时地同步到APK中,从而可以大规模降低开发复杂度。
优势四:允许自定义拦截器,ARouter是支持拦截器的,而拦截器其实就是AOP的实现,可以自定义多个拦截器解决一些面向行为编程上出现的问题。
优势五:ARouter可以提供IoC容器,IoC其实就是控制反转,这一部分做过服务端开发的朋友可能比较了解,因为服务端开发经常用到的Spring框架能够提供的一个非常重要的能力就是控制反转。
优势六:映射关系自动注册,在页面不是很多的小型APP上面,自动注册并不会体现出太大优势,但是对于大型APP而言,可能页面数量已经达到的几十个或者数百个,在这样的情况下,自动注册就显得非常重要了,因为不可能将每一个页面都通过代码的方式进行注册。
优势七:灵活的降级策略,ARouter可以提供很多种降级策略供用户自行选择,而原生的路由方案存在无法灵活降级的问题,StartActivity()一旦失败将会抛出运营级异常。
Route Service Interceptor API
root1 interceptor1 provider1
依赖住的实现
。而现在使用的这种场景只需要对外暴露出一个activity,然后在这个activity中注册一个intent-filter,这样之后所有的外部路由请求都会经过这唯一的门,然后在这个activity中获取到URL并将其交给ARouter,剩下的就由路由框架做分发了。
activity URL ARouter
。在实现时需要声明出需要进行解析的字段,其名字会映射到外面的URL的参数上,然后需要将其标注好Autowired这样的注解,Autowired注解中有一个属性就是name,相当于别名,标注了别名之后ARouter会自动提取别名所对应的参数。
而Service的实现就是具体的业务功能,这部分也需要通过IoC容器进行获取。这样整个的流程将通过用户的直接依赖转化成通过控制反转容器依赖的这种形式。
Ioc
这样就完成了模块间的解耦,因为完全没有依赖到服务的具体实现,而服务的具体实现的控制权完全掌握在IoC容器的Route层。
几点看法: 1、 ARoute的核心是通过APT在dex文件中添加*$$*.java,在loadinto函数里配置或读取uri与类路径的对应关系; 说到底还是本地逻辑,不够灵活; 2、如果真想实现作者的解耦目的, 我建议通过后台接口实现; 即服务端配置uri和类路径的对应关系, 客户端通过网络连接获取并解析。 既能实现类之间的解耦, 也更加灵活可配置。 3、不适用于插件化的app, 因为ARoute无法打开未安装包中的组件, ARoute只能打开已安装app的组件;
URL ARouter
阿里开源路由框架ARouter的源码分析
https://juejin.im/entry/6844903505937842183
注解 组分类
使用注解,实现了映射关系自动注册 与 分布式路由管理
编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
映射关系按组分类、多级管理,按需初始化
灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常
自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用 == 对动作的路由
支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
支持获取Fragment
支持多模块使用,支持组件化开发
支持多种方式配置转场动画
支持MultiDex(Google方案)
annoations
compiler
interceptor
root1 group
build.navigation
URi Sche
/*这种使用URi的方式中,URi的Scheme 和 host不影响结果,可以随便设,关键的是path
* - build(URI)会把URI解析为path,并把当前URI存入PostCard
* - build(String)构造的PostCard不存储URI*/
Ioc
.navigation.sayHello
总的来说,就是arouter-annotation实现了路由表结构的定义,arouter-compiler在编译期完成了 构造路由表逻辑的创建, arouter-api在运行期加载逻辑构建路由表,并实现路由控制
Route
@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;
}
RetentionPolicy
Autowired自动装载注解
Interceptor priority name
RouteMeta 路由元信息
ackpass 提交一版1.9.7
RouteMeta
RouteType
IProvider ContentProvider Fragment
className
TypeKind {
}
arouter-compiler
开源最佳实践:Android平台页面路由框架ARouter中“二、ARouter的技术方案”的“页面注册:注解&注解处理器”
1. 首先通过注解处理器扫出被标注的类文件;
2. 然后按照不同种类的源文件进行分类,这是因为ARouter是一个框架,其能够提供的功能非常多,所以不仅仅提供了跳转功能,它也能够实现模块之间的解耦,除此之外ARouter还能够提供很多的功能,像刚才提到的拦截器可以实现自动注册,其实ARouter中的所有组件都是自动注册的
3. 在按照不同种类的源文件进行分类完成之后,就能够按照固定的命名格式(工程名+$$+Group+$$+分组名)生成映射文件,这部分完成之后就意味着编译期的部分已经结束了
atlas
(Map< String, RouteMeta> atlas(Map< String, RouteMeta> atlas
IRouteGroup routes在键盘每个的组
ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量
root
目标Class都实现了IProvider接口,借此实现部分路由转到该清单中
需要注意的是:Ioc动作路由清单其实只是 Route注解的一种特殊用法,总的来说,还是一种URL与目标类的映射关系
providers
$$Providers$$模块名
工程名$$Interceptors$$模块名 【模块内的拦截器清单列表】
Map< Integer, Class< ? extends IInterceptor>> interceptors
interceptors
RouteProcessor
ARouter Group service
Providers$$模块名
Interceptor app
ARouter Root app
很明显,ARouter$$Group$$service和ARouter$$Group$$test分别是分组 service 和 test 的组内路由清单列表,我们可以把它想象成分别是 1班 和 2 班 的学生名单。
ARouter$$Root$$app 则是组别的清单列表,我们亦可以把它想成班级的清单,里面有两个班级,1班 和 2 班。
工程名$$Group$$分组名 【组内的路由清单列表】
工程名$$Root$$$模块名 【组别的清单列表】
一般来说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中的模板不能随意更改
阿里开源路由框架ARouter的源码分析
https://juejin.im/entry/6844903505937842183
substitute.getArguments
HelloService SerializationService IProvider
getIntent getArguments
//
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() + "]");
}
}
IRouteRoot IInterceptorGroup IProviderGroup getConstructor.newInstance.loadInto Warehouse.providerIndex
需要注意的是:初始化阶段只加载了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<>();//已排序的拦截器实例对象
//```
}
PathReplaceService
Ioc byType
} 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));
//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对象 meta.getPath meta.getGroup
setArguments postcard.getExtras
private IProvider provider; // Nullable 当使用Ioc的时候,结果会导致该变量被赋值为目标对象实例
IProvider provider;
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();//实例化【组内清单创建逻辑】
iGroupInstance.loadInto(Warehouse.routes);//将该组的【组内清单列表】加入到内存仓库中
Warehouse.groupsIndex.remove(postcard.getGroup());//从【组别的清单列表】移除当前组
providers.put providerMeta,provider
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调用
Ioc.byName
iprovider理解为 主要针对“对外提供api的服务”