ARouter 源码分析
概述:
arouter-annotation: ARouter路由框架所使用的全部注解,及其相关类
arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译期完成了 构造路由表逻辑的创建
arouter-api:在运行期加载逻辑构建路由表,并实现路由控制
1.arouter-annotation注解
1.1 @Route路由注解
@Route 是 ARouter 最重要的注解,也是路由最基本的节点,该注解主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。
值得说明的一点是 ARouter 并非仅提供页面(Activity)的路由功能,还可以用来路由模块想要暴露给其他模块调用的接口。
也就是说 @Route 不仅可用于 Activity 类,还可用于模块对外接口的实现类,实现类似于 AIDL 的功能,也就是IOC
1.2 @Interceptor拦截器注解
@Interceptor 是拦截器注解,拦截器是全应用全局的,不分module,只要集成进apk就起效
1.3 @Autowired自动装载注解
@Autowired 是页面跳转时参数传递用的。目标Class中使用该注解标志的变量,会在页面被路由打开的时候,在调用Inject(“`)后自动赋予传递的参数值
1.4 RouteMeta路由元信息
如果全部路由信息认为是一张表格,那么RouteMeta就是表格的一行,代表路由表的一条元信息
2.arouter-r注解编译器
实现了“自动注册映射关系”也就是在编译期间自动生成映射文件,所以该module其实就是实现了一些注解处理器,目标在于生成映射文件与辅助文件(构造路由表逻辑的创建)
2.1 组别的清单列表 【工程名&&Root&&模块名】
Map< String, Class< ? extends IRouteGroup>> routes
包含了组名与对应组内的路由清单列表Class的映射关系
是Arouter的“分组管理,按需加载”的实现。
ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量
那么什么时候加载分组结点呢?其实就是当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去,这就是ARouter的按需加载
2.1.1 组内的路由清单列表 【工程名&&Group&&分组名】
(Map< String, RouteMeta> atlas
包含了对应分组下的,路由URL与目标对象Class的映射关系;
注意Router注解中无分组的话,默认以“/xx/xx”的第一个xx为分组名
ARouter$$Root$$core
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$core implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("core", ARouter$$Group$$core.class);
routes.put("degrade", ARouter$$Group$$degrade.class);
routes.put("service", ARouter$$Group$$service.class);
}
}
degrade 特殊,同时在provider和group里?
2.2 Ioc的动作路由清单列表 【工程名&&Providers&&模块名】
Map< String, RouteMeta> providers
PROVIDER 类型的路由节点的清单列表
包含了使用依赖注入方式的某class(实现了IProvide接口的直接子类)的 路由URL 与class映射关系
目标Class都实现了IProvider接口,借此实现部分路由转到该清单中
需要注意的是:Ioc动作路由清单其实只是 Route注解的一种特殊用法,总的来说,还是一种URL与目标类的映射关系
其实想一下,依赖注入,无非也就是指定好目标接口的目标类,然而实例化后进行赋值。URL就是指定说明
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Providers$$core implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.vivo.test.core.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloService.class, "/service/hello", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/service/pathrepalce", "service", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/degrade/service", "degrade", null, -1, -2147483648));
}
}
2.3 模块内的拦截器清单列表 【工程名&&Interceptors&&模块名】
Map< Integer, Class< ? extends IInterceptor>> interceptors
包含了某个模块下的拦截器 与 优先级的映射关系
一个模块下的所有拦截器都在该类中包含,无分组特性,所以直接以模块名命名类文件
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Interceptors$$core implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(8, TestInterceptor.class);
}
}
root 节点是包含activity,service,degrade 单独是一个service?
2.4 Autowired注解处理
分析下TestActivity1自动生成的路由辅助文件。
通过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指向的类实例并赋值
3. arouter-api路由控制
最基础的就是Compiler这个SDK,其内部有三个处理器,分别是:Route Processor,Interceptor Processor以及Autowire Processor,通过名字就可以看出这三个处理器分别是处理路径路由、拦截器和进行自动装配的。而API的SDK是用户在运行期使用的,这一部分主要分为四层。
最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
在Launcher层的下一层就是Frossard层,从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template,这里的Service概念和服务端的Service概念是相似的,也是在客户端的简单引申,但是却不同于Android组件中的Service,这里的Service是ARouter抽象出来的概念,从本质上讲,这里的Service是接口,从意义上讲是将一定的功能和组件封装成接口,并对外提供能力。Template则是模板,主要用于在编译期执行的SDK,这个SDK会在编译期生成一些映射文件,而这些映射文件会按照Template组件中提供的模板来生成,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。
再往下一层就完全是SDK的内部实现了,这一层包括了Ware House、Thread、Log、Exception以及Class工具。Ware House主要存储了ARouter在运行期间加载的一些配置文件以及映射关系;而Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的;Class工具则是用于解决不同类型APK的兼容问题的。
再下一层就是Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层,当然也会按照功能模块进行划分。
3.1 init过程
3.1.1 ARouter.init()
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
3.1.2 _ARouter.init(application)
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
3.1.3 LogisticsCenter.init(mContext, executor);
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//billy.qi modified at 2017-12-06
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
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);
}
}
...
} catch (Exception e) {
...
}
}
3.1.4 Warehouse 路由仓库
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// 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<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
33 - 43 行:分组管理,按需加载
初始化时会加载内存应用的组别的清单列表、Ioc的动作路由清单列表、模块内的拦截器清单列表到路由仓库中Warehouse中,组内的路由清单列表此时并不会加载。
3.1.5 _ARouter.afterInit() 初始化拦截器控制器
根据 Ioc.ByName()方式获取拦截器控制器,注意这个拦截器并不是我们定义的拦截器,而是Arouter实现的拦截器逻辑,它持有我们定义的拦截器,可以理解为“拦截器截面控制器
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
...
}
}
}
12-15行:拦截器控制器的主要逻其实就是获取Warehouse中所有的拦截器,在需要拦截时执行他们的拦截方法。
3.2 路由寻址过程
ARouter.getInstance().build("/core/activity").navigation();
3.2.1 build Postcard
// ARouter.build
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
// _ARouter.build
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
protected Postcard 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);
}
}
第9和21行分别是2种build Postcard的方式,可以看到build(path)也是调用了build(String path, String group)。如果采用的是build(path),group字段默认取"/group/path/“第一个”/“和第二个”/“之的字段,当有多个”/group/path/childpath/",第二个"/"之后都算path。
13、25行 展示了PathReplaceService 的工作原理
29 完成创建Postcard
3.2.2 navigation 过程
省略Arouter调用过程,直接看最终的_ARouter.navigation,一次路由的跳转包含查找回调的调用、拦截器处理、绿色通道校验、和具体路由操作。
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
...
if (null != callback) {
callback.onLost(postcard);
} else { // No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) { // 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);
}
/**
* 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);
}
return null;
}
第3行:完善路由信息,详细分析见3.2.4节
6-13行:路由异常时处理,可以看到,手动添加了降级处理的callback要优先于全局的降级处理全局的降级处理,在没有主动添加降级处理时才会触发全局的降级。
22-47行:拦截器的工作原理,在没有设置为greenChannel时,会调用到拦截器控制器的doInterceptions,循环遍历所有的拦截器处理拦截逻辑,结合3.1.5内容理解。
第49行:真正的路由过程
3.2.3 真正的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:
// 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);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
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;
}
5-30行:处理Activity的跳转,可以看到ARouter底层启动Activity时也是通过系统startActivity方法。同时在这里会设置intent的action,flag,另外postcard.getDestination() 返回的其实是.Class对象。
33-47行:处理PROVIDER、BOARDCAST、CONTENT_PROVIDER、FRAGMENT,返回一个对应类的实例,如果是Fragment,则返回实例,并填充bundle,另外这里的provider指的是ARouter的服务IProvider。
3.2.4 完善路由信息 LogisticsCenter.completion(postcard)
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
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 {
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // Reload
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
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);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
6 - 23行:
1.根据路径URL获取到路径元信息
2.如果未获取到路径元信息,可能是由于 未加载对应分组的【组内清单列表】 or 的确没有
3.从【组别的清单列表】拿到对应组的组内清单创建逻辑,如果为空,则丢出异常,未找到,不为空时,走如下逻辑(15 - 18行):
(1)实例化【组内清单创建逻辑】
(2)将该组的【组内清单列表】加入到内存仓库中
(3)从【组别的清单列表】移除当前组
(4)第23行重新加载路径元信息
26 - 29行:
1.设置目标 class
2.设置路由类型
3.设置路由优先级
4.设置额外的配置开关信息
31 - 50行:
处理uri的跳转,如果有URI,则根据路由元信息的目标Class的需要注入的参数
1.获取参数名和value
2.获取参数名和类型 (定义参数类型的类 TypeKind)
3.将参数名和类型和值放到postcard对应的属性里。
53 - 76行:处理不同的路由类型
54 - 71:PROVIDER类型的路由则实现实例化目标类绿色通道(byType方式的核心实现),可以看到Arouter所有的服务都是单例的。
73:如果是Fragment则设置绿色通道,不做任何拦截
4.其他
4.1 CallBack
InterceptorCallback 拦截器的callbck
NavigationCallback 降级逻辑的全部回调
NavCallback 降级逻辑的部分回调
4.2 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调用
参考:
https://yq.aliyun.com/articles/71687?t=t1
https://blog.****.net/fei20121106/article/details/73743235