ARouter 源码分析
Posted 薛瑄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARouter 源码分析相关的知识,希望对你有一定的参考价值。
一、前言
在开始分析ARouter的源码之前,我假定你已经知道ARouter 的用途,并且会熟练使用。下面我在官方demo的基础上,来分析每项功能的流程。
demo 中的 三个 Module
- app demo项目的 主module
- module-java demo项目的 子module
- module-kotlin demo项目的 子module
下面三个Module 提供了核心功能
- arouter-annotation 使用到的注解相关信息,例如:@Route, @Interceptor,RouteMeta 等
- arouter-api 主要的代码逻辑
- arouter-complier 处理注解,生成相应的类。(你可能需要在编译期调试注解处理器)
下面两个module 是方便ARouter的使用以及开发
- arouter-gradle-plugin 进行路由表的自动加载, 默认通过扫描 dex 的方式 进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问 dex 文件。使用到字节码插桩相关知识
- arouter-idea-plugin idea 插件,可以在跳转代码的行首找到一个图标 (navigation) 点击该图标,即可跳转到标识了代码中路径的目标类(在插件市场中搜索 ARouter Helper)。
我们在本篇的分析过程中,我们只分析主流程。涉及到类生成,字节码插桩 插入的函数,我们直接查看对应的类即可。它们的生成过程,后续分析
二、 arouter-annotation源码分析
2.1 @Route路由注解
@Route 是 声明路由的注解,主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。
该注解可用于继承了 Activity,Fragment,IProvider(Ioc的接口,下文会介绍)的等子类上,在经过arouter-complier处理后,生成类的命名方式会有所不同
@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;
2.2 @Interceptor拦截器注解
@Interceptor 是拦截器注解,拦截器是全应用全局的。可设置优先级,对所有路由都生效
该注解用于实现IInterceptor 的类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
2.3 @Autowired
@Autowired 是给变量自动赋值的。可以在界面跳转的时候参数传递,也可当做依赖注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface Autowired
// Mark param's name or service name.
//如果指定的是service名称,就是在当做依赖注入使用
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 "";
2.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
三、arouter-compiler 生成的类介绍
下图是官方demo,编译后生成的类
- 在自动生成的routes 文件中,主要是注解@Route、@Interceptor生成的类,这些类负责加载路由目标类、拦截器、Provider等等,下面按功能分析一下
-
分组的路由表 —— 类名组成:工程名 Group 分组名, 继承了 IRouteGroup
- 每一个路径对应一个RouteMeta对象
- 默认以“/xx/xx”的第一个xx为分组名
-
根路由——类名组成:工程名Root模块名 , 继承了 IRouteRoot
- 每个分组名,对应各自的分组路由表
- ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去
-
Ioc 路由表——类名组成:工程名Providers模块名 , 继承了 IProviderGroup
- 每一个实现了IProvider的接口 对应一个RouteMeta 对象,RouteMeta 对象的destination值 是 该接口的实现类
- Ioc动作路由清单其实只是 Route注解的一种特殊用法,也是一种URL与目标类的映射关系
-
拦截器——类名组成:工程名Interceptors模块名 , 继承了 ISyringe
- 包含了某个模块下的拦截器 与 优先级的映射关系
- 项目目录testactivity下,是使用@Autowired 注解后,生成的类——类名组成:类名工程名Autowired , 继承了 IRouteRoot
- 所有使用了Autowired 的变量所在的类,会自动这种类,主要作用就是为类里的变量赋值 (做过后台开发的应该比较熟悉)
注意,这些自动生成的类,都继承了arouter-api 中的相关接口,下文会分析这些接口在何时被调用
四、arouter-api 源码流程分析
下图摘自
先来整体认识一下,这个module
-
最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
-
接着是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 获取包名、资源名、系统等。
-
Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层
下面就从一个简单的使用,来逐步深入源码
ARouter.init(getApplication());
4.1、初始化
ARouter 的所有函数的实现,都交给了_ARouter,先初始化_ARouter
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/service/interceptor"
//因为这个流程与界面跳转的流程,基本一致,所以这里暂不分析,可参考4.2 的分析
_ARouter.afterInit();
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
protected static synchronized boolean init(Application application)
mContext = application;
//初始化LogisticsCenter,就是把编译器生成的那些路由信息加载,此时之后加载root路由、拦截器、provides
LogisticsCenter.init(mContext, executor);
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
LogisticsCenter类
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException
mContext = context;
executor = tpe;
try
//下面会重点分析这个方法
loadRouterMap();
//判断是否由自动生成的代码注册,这里自动生成的代码是指通过arouter-register项目使用 字节码插桩 添加的代码,
//它们的功能是初始化各个module中的root路由、拦截器、provides。
if (registerByPlugin)
//如果为true,就说明在loadRouterMap()中已经初始化了
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
else
//通过下面的代码初始化
//com.alibaba.android.arouter.routes 路径下的所有类名称会被添加到这个变量中
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context))
// These class was generated by arouter-compiler.
//查找com.alibaba.android.arouter.routes 路径下的所有类(该路径就是第三点分析的routes文件夹)
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
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
//根据不同的类名,强制转为不同的类型,进行初始化
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)
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
arouter-register项目使用 字节码插桩 添加的代码,就是添加到函数loadRouterMap()中,去加载所有module下的com.alibaba.android.arouter.routes的root路由、拦截器、provides,会调用到register函数中
LogisticsCenter类
private static void loadRouterMap()
registerByPlugin = false;
//auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
private static void register(String className)
if (!TextUtils.isEmpty(className))
try
Class<?> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
//上文说过,这些自动生成的类,都继承了atouter-api的相关接口,这里根据接口类型,进行不同的初始化
if (obj instanceof IRouteRoot)
registerRouteRoot((IRouteRoot) obj);
else if (obj instanceof IProviderGroup)
registerProvider((IProviderGroup) obj);
else if (obj instanceof IInterceptorGroup)
registerInterceptor((IInterceptorGroup) obj);
else
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
catch (Exception e)
logger.error(TAG,"register class error:" + className);
这里只分析加载根路由的过程,registerProvider、registerInterceptor过程与它类似
private static void registerRouteRoot(IRouteRoot routeRoot)
//把registerByPlugin 设置为true
markRegisteredByPlugin();
if (routeRoot != null)
//调用根路由的loadInto 函数,路由组信息加载到Warehouse.groupsIndex
routeRoot.loadInto(Warehouse.groupsIndex);
这里以demo中主module的根路由为例
public class ARouter$$Root$$app implements IRouteRoot
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes)
// 以路由组名 为key,对应的类class 为值,存入Warehouse.groupsIndex
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
4.2、ARouter#build
下面从这行代码,来分析主流程
ARouter.getInstance().build("/test/activity2").navigation();
getInstance 获取ARouter的单例,就不用多说了,直接从build 开始分析
protected Postcard build(String path)
if (TextUtils.isEmpty(path))
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
else
// 这里的navigation 就是根据 by type 获取Provide 的方式
// 下文会介绍 by type 获取Provide 的源码
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService)
//如果是实现了,这个Provide,就表示需要对路径进行替换
path = pService.forString(path);
return build(path, extractGroup(path), true);
protected Postcard build(String path, String group, Boolean afterReplace)
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group))
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
else
if (!afterReplace)
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService)
path = pService.forString(path);
//创建Postcard 对象,一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程
return new Postcard(path, group);
4.3、Postcard#navigation
Postcard类
public void navigation(Activity mContext, int requestCode, NavigationCallback callback)
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
ARouter类
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback)
//又调用到_ARouter 中
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
4.4、_ARouter#navigation
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
//获取预处理的Provide
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果不为空,就执行
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard))
// Pretreatment failed, navigation canceled.
return null;
try
// 加载本次路由的路由组,并设置关于路由的更多信息到postcard
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.
//通过by type 的方式获取DegradeService 实例,进行降级处理,这是全局降级
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService)
degradeService.onLost(context, postcard);
return null;
//如果在调用 navigation 的时候,设置了回调函数
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)
//拦截器的处理结果是中断
//如果在调用 navigation 的时候,设置了回调函数
if (null != callback)
callback.onInterrupt(以上是关于ARouter 源码分析的主要内容,如果未能解决你的问题,请参考以下文章