本文主要是阅读源码,不会说组件化带来的好处以及为何需要组件化之类的问题。主要是了解ARouter的最核心的逻辑,包括页面跳转,服务注册等最基础的功能。关于如何使用ARouter本文也不会具体的讲解,github地址:ARouter,本文是基于最新版本。
阅读本文需要有一定的ARouter使用经历,同时对于组件化也有一定的认识。
题外话:目前ARouter已经算是不再维护的状态了,所以大家可以只是去学习一下ARouter的组件化思维与实现方式即可,对于在项目中使用是不再推荐了,推荐可以使用TheRouter
页面跳转
我们从最基本的页面跳转讲起。假如我们有一个页面,它定义的路由地址path
是test/main
,那么我们就可以通过:
1 | ARouter.getInstance().build("test/main").navigation(); |
进行跳转。
我们先看下他是如何跳转的,我们发现它的最终调用是来到了_ARouter
的navigation
,我们具体来看下方法:
1 | protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { |
可以看到,无聊是否经过拦截器,最终都会调用_navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback)
进行跳转,源码如下:
1 | private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) { |
其中还有一个关键的LogisticsCenter.completion(postcard)
方法,他的作用是用于判断这是否是一个有效的注册的路由,具体的解析我们在说了Warehouse
的相关属性之后再说,在下面completion分析。
综上我们就可以看到调用ARouter.getInstance().build("test/main").navigation();
的大概流程
ARouter.getInstance().build(path)
:返回一个Postcard
对象- 调用到
_ARouter
的navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
,进行跳转前的最后处理,比如有消息,拦截器等 - 调用到
LogisticsCenter.completion(postcard);
,有效性的判断 - 调用到
_navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback)
,最终根据类型进行处理及跳转
注解&接口初认识
首先,在ARouter中,有三个常用到的注解
@Route
,路由,包括我们的Activity/Fragment/Provider的配置都是通过使用该注解实现。其中服务(Provider)的还需要实现IProvider
接口@Interceptor
,拦截器,假如我们需要对于一些页面在跳转之前进行进行检测,符合条件才跳转,则可以通过使用该注解实现,同时需要实现接口IInterceptor
@Autowired
,假如我们有需要再通过ARouter跳转传递参数的时候,直接在对应接受页面的成员变量直接设置(即不通过intent再去取值),对应的参数就可以使用这个注解进行注解,记得在对应页面调用ARouter.getInstance().inject(this);
才会生效
还有就是ARouter中定义的一些关键的接口以及它的作用:
IProvider
:该类是我们需要实现跨模块调用功能的时候,需要实现的类,在本文也叫做服务(Provider)。具体的功能后续再说1
2
3
4
5
6
7public interface IProvider {
/**
* 会优先于其他方法调用,每一个服务只会调用一次
*/
void init(Context context);
}IProviderGroup
,这个接口是用于给arouter-compiler
生成代码的时候,把对应模块的所有的Provider
服务存入map,它们最终都是存入了Warehouse.providersIndex
中1
2
3
4
5public interface IProviderGroup {
//对应module的所有的实现了IPrivder同时使用了@Route注解的实现类,会存入providers中,就算是group不同也一样存入
// 关于它的key值,我们后续篇章说,value就是Provider的实现类以及对应的一些信息
void loadInto(Map<String, RouteMeta> providers);
}IRouteGroup
,这个接口是用于给arouter-compiler
生成代码的时候,以group
为分组,把所有的使用了Route
注解的实现类存入map中,包括我们的Activity/Fragment
还有IProvider
,他们最终都是存入了Warehouse.routes
中,每个模块可以有多个IRouteGroup
实现类,就是可以有多个分组路由。1
2
3
4public interface IRouteGroup {
// map的key是路由的全路径,RouteMeta为具体的类以及相关分组信息
void loadInto(Map<String, RouteMeta> atlas);
}IRouteRoot
,这个接口是用于给arouter-compiler
生成代码的时候,用于把group的值作为key,实现了IRouteGroup
的生成实现类作为values的数据存入到方法参数routes中,其实就是把所有的IRouteGroup的实现类,按照分组存入map中,也就是Warehouse.groupsIndex
中的值,每个模块都只有一个IRouteRoot
实现类1
2
3
4public interface IRouteRoot {
//key是group,value是具体的IRouteGroup实现类,到时候调用的时候通过反射的方式调用IRouteGroup中的loadInto接口(普通初始化)
void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}IInterceptor
,该接口是给需要实现拦截器效果的模块去实现,使用类需要以@Interceptor
注解,在拦截器讲解篇章中1
2
3
4public interface IInterceptor extends IProvider {
//根据callback回调该postcard是可以继续执行还是拦截中断掉
void process(Postcard postcard, InterceptorCallback callback);
}IInterceptorGroup
,这个接口是用于给arouter-compiler
生成代码的时候,用于存储IInterceptor
的相关实现类,具体存放是在Warehouse.interceptorsIndex
中ISyringe
,这个接口是用于给arouter-compiler
生成代码的时候,实现具体的参数注入,它的实现类是通过反射调用的(所以存在一定的耗时),具体的我们后续再说(服务IProvider)。。1
2
3
4public interface ISyringe {
// 需要注入的对象,比如是Activity/Fragment
void inject(Object target);
}
数据类作用初认识
RouteMeta类
他是描述路由信息,或者是说统一了所有的使用了@Route
注解生成的信息,方便了不同类型都可存入Map中,比如我们的Activity/Fragment/IProvider
,他们的路由地址与实现类(启动类)的映射关系。下面是具体的参数信息:
1 | public class RouteMeta { |
RouteMeta不仅仅是用于描述注解的生成,帮助我们创建doc,创建Provider以及页面路由的java描述属性文件,还是我们的Postcard
的父类,Postcard
是我们在进行路由创建页面跳转的时候使用的描述Bean。
Postcard类
我们通过ARouter.getInstance().build("")
的时候就会创建一个Postcard
对象,他的具体的属性解析如下
1 | public final class Postcard extends RouteMeta { |
Warehouse类
该类存储了各式各样的信息,包括我们的所有路由,所有的Provider,拦截器,以及一些用于缓存的List
1 | class Warehouse { |
我们逐个说。
groupsIndex
:它的key是分组名称(也就是group),value是IRouteGroup的实现类的Class对象,该对象是给IRouteRoot
的实例调用它的loadInto
方法的时候传入,每一个模块,都只有一个IRouteRoot
的实现类。某个模块的IRouteRoot
实现类中的loadInto
方法会把所有该模块的不同分组生成的IRouteGroup实现类存入到groupsIndex
中,这些写入的操作会在ARouter初始化的时候完成(关于初始化的流程后续说)。然后我们在路由跳转(nativation)的时候,会读取groupsIndex
中该路由的分组对应的IRouteGroup
的实现类,通过反射调用实现类的loadInto
方法,把该分组的所有的路由配置存入到routes
中。然后再移除掉groupsIndex
中对应的key-value键值对。routes
:它的key值是路由的path,value值是RouteMeta,它的作用是用于缓存所有我们跳转过的路由,不必我们每次都去查找。它的取值是来自于groupsIndex
,当查找到对应的IRouteGroup
实现类,就会把该类(即该路由分组)的所有路由表存入到routes
。然后后续跳转的时候假如routes
有就不查找,无就查找。providersIndex
:它的key正常来说是IProvider
的实现类的类名(会有重复的问题,后续再说),value是RouteMeta类,存储了实现类的路由路径,class等信息。它是用于快速查找具体的服务的(即根据实现类的name),可以通过ARouter.getInstance().navigation(Class)
拿到服务实现类,当然我们也可以通过传入服务的路由path
实现也行,它也是Arouter初始化的时候把所有的服务实现类存入到自身中,初始化(后续再说具体的ARouter流程)的时候调用IProviderGroup
是实现类的loadInto
方法,传入该参数。providers
:属于服务类型的路由只要经过了LogisticsCenter.completion
方法,就会把创建的服务换成存入到这个map中,key是路由的path,value是具体的实现类对象。当我们通过路由获取一个服务的时候,经过了LogisticsCenter.completion
方法,假如providers
中有就会直接返回,不存在就会通过反射创建,然后存入到providers
中。interceptorsIndex
:它的key是优先级
,value是IInterceptor
的实现类。该对象会在初始化的时候,调用IInterceptorGroup
的实现类的loadInto
方法作为参数传入,通过这个方法把所有的IInterceptor
的实现类存入。每一个模块至多多一个IInterceptorGroup
的实现类,它是通过注解生成的。关于它的使用我们在讲拦截器的时候再说。数字越小优先级越高,不可以有相同优先级的拦截器。interceptors
:它是所有的拦截器的实现类的内存缓存,在初始化InterceptorServiceImpl
的时候,异步从interceptorsIndex
中取出所有的IInterceptor
的实习类的Class
对象,通过发射生成对象存入到interceptors
中。然后再我们调用interceptors的时候,进行拦截,具体在拦截器篇章中说。
核心流程分析
路由分组
在开始较为核心的一些流程之前,我们先看下ARouter是如何分组的,并且为何需要分组,然后是建议我们如何使用分组的。我们构建路由都是必须使用到build
方法,框架提供的有三个
1 | 1. `build(String path)` |
我们可以看到1和2构建Postcard
对象,都是通过extractGroup()
方法来返回组名称的。
1 | // 获取group |
我们使用@Route
只使用path
,不使用group
的时候,我们的路由注解生成类,即IRouteGroup
的实现类的例子如下(关于IRouteGroup):
1 | // 只用path |
可以看到它的组别就是path
中的第一个/
与第二个/
之间。这也是ARouter推荐使用的,我们可以不使用group
参数,但是我们的path
必须是以/
开头,与第二个/
之间的字符串不能为空,才算是一个有效的路由。然后路由的path还是原值。
第二种,则是带有group
参数,就需要使用上面的第三个类型的build
方法,例如:
1 | // 制定路由的group |
通过注解制定group
的组件(包括Activity/Privider等),在跳转or获取服务对象的时候,就需要传入group
参数了,这种情况,它的path
就没有说必须以/
开头。
我们一般是推荐使用第一种方式,直接以path
参数的第一个/
与第二个/
之间作为组。然后呢,每一个模块虽然可以生成多个IRouteGroup
,但是我们建议每一个模块统一使用一个group,这样方便知道是属于那个模块的。
初始化
经过上面的一些了解,我们现在正式开始了解整个ARouter的工作原理,包括它的初始化,跳转,服务,拦截器等。我们会通过流程图+源码的形式来说明。
首先是我们的ARouter初始化。目前ARouter的初始化有两种方式,一种是通过gralde的plugin,通过ASM插桩实现,这种会效率更高,速度更快,也推荐使用,另一种是直接调用java代码直接调用查找实现。本文是会先讲解第二种,因为它的流程会更清晰,第一种的后续再说。
首先我们需要知道ARouter的初始化是需要干什么?它的初始化就是把我们的路由表给加载进来,加载进来之后我们才可以去使用,更直接的说,是对Warehouse
中的变量进行赋值,然后才开始使用我们ARouter中的功能。首先看最基础的流程:
可以看到,最终的路由加载是在LogisticsCenter.init(Context context, ThreadPoolExecutor tpe) throws HandlerException
方法中,在看这个方法的源码之前,我们先看一下ARouter通过注解生成的一些关键类,这些类都是需要加载的类。
- 每一个模块,都会通过compiler生成一个
IRouteRoot
的实现类,用于存储所有的IRouteGroup
的Class对象。他的命名的规则是ARouter$$Root$$
+modulename
,modulename
就是我们在每个模块的gradle下配置的AROUTER_MODULE_NAME
,比如官方例子中的arguments = [AROUTER_MODULE_NAME: project.getName()]
1
2
3
4
5
6
7
8public class ARouter$$Root$$modulejava implements IRouteRoot {
// 同一个模块的所有的IRouteGroup的实现类都会存入到Warehouse.groupsIndex中
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("m2", ARouter$$Group$$m2.class);
}
} - 每一个模块,都会通过compiler生成一个
IProviderGroup
的实现类,用于存储该模块所有实现了IProvider
接口的并且被@Route
注解的类,生成类的命名规则是ARouter$$Providers$$
+modulename
,每一个模块只会生成一个IProviderGroup
的实现类。1
2
3
4
5
6
7
8
9public class ARouter$$Providers$$modulejava implements IProviderGroup {
// 所有的实现类都会存入到Warehouse的providersIndex中
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, ...));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class,...));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, ...));
}
} - 每一个模块,有使用到拦截器
@Interceptor
注解的实现了IInterceptor
的实现类,就会生成一个IInterceptorGroup
的实现类,它的命名规则是ARouter$$Interceptors$$
+modulename
,每一个模块最多只会生成一个IInterceptor
的实现类1
2
3
4
5
6
7
8
9
10// 这是拦截器的,使用@Interceptor注解的类,例如这里的Test1Interceptor & TestInterceptor90
// 都会存入到interceptors,这个就是Warehouse中的interceptorsIndex
public class ARouter$$Interceptors$$modulejava implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
// key是优先级,value是实现类的class
interceptors.put(7, Test1Interceptor.class);
interceptors.put(90, TestInterceptor90.class);
}
}
我们的初始化主要审设计的三个生成类,即每一个模块都只有一个的IRouteRoot
+IProviderGroup
+IInterceptorGroup
,通过获取到每个模块的这三个,调用它们的loadInto
方法,就可以组织起路由表了,这就是LogisticsCenter.init
做的事情,接下来我们看源码:
1 | public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { |
其中是通过ClassUtils.getFileNameByPackageName(Context context, final String packageName)
获取到com.alibaba.android.arouter.routes
为前缀的类名,它的主要逻辑是通过读取本地的dex,通过DexFile.loadDex()
拿到所有的class,假如与传入的packageName匹配就加入到return的List中。
经过上面的初始化,我们Warehouse
中的groupsIndex,interceptorsIndex,providersIndex
都已经赋值了。
服务IProvider
我们这里先说服务,它其实是代理模式的一种实现,通过IProvider
接口,我们可以实现跨模块的功能调用,而不产生代码的耦合。它的一般使用方式是:在最底层的module依赖中定义一个接口,该接口继承自IProvider
,在具体模块实现该接口并使用@Route
注解。例如,moduleA有一个获取用户信息的接口,然后moduleB需要调用,这时候,我们会在最底层定义一个获取用户信息的接口:
1 | public interface IModuleAProvider extends IProvider{ |
然后模块A中实现该接口:
1 | @Route(path= "modulea/provider") |
在moduleB中,就可以通过下面两种方式获取到ModuleAProvider
对象。
ARouter.getInstance().navigation(IModuleAProvider.class);
ARouter.getInstance().build("modulea/provider").navigation();
我们先说下第一种,看下navigation(Class<? extends T> service)
的实现。
1 | protected <T> T navigation(Class<? extends T> service) { |
可以看到它是从我们的providersIndex
中取的,假如有该service的class name对应的,就使用path & group
构建Postcard
对象返回。然后这里就说下我们providersIndex
中的key的规则,上面的IProviderGroup解析和IProviderGroup的实现类,我们都有提到存入到providersIndex
的key,它的规则如下:
- 假如是直接实现了IProvider,则是key是实现类的名称
- 假如是间接了IProvider,则key是父接口的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//这里存入到该模块的IProviderGroup实现类中的key值是TestProvider的类全路径
@Route(path = "/test/provider")
public class TestProvider implements IProvider{
@Override
public void init(Context context) {
}
}
//
public interface HelloService extends IProvider {
}
//这里存入到该模块的IProviderGroup实现类中的key值是HelloService的类全路径,而不是HelloService2
@Route(path = "/test/provider2")
public interface HelloService2 implements HelloService {
@Override
public void init(Context context) {
}
}
一般使用,我们都会通过在最底层中定义一个接口,该接口继承自IProvider
,然后对应需要对外提供功能的模块实现该接口并使用@Route
注解,即上面的第二种情况。
通过上面的key分析,我们就知道可以直接通过navigation(Class<? extends T> service)
查找到对应的Postcard
了,得到实现类的路由path
&group
。最终是通过LogisticsCenter.completion(postcard)
,给postcard中的provider
赋值。
LogisticsCenter.completion方法
我们在上面开始的路由跳转就谈到了LogisticsCenter.completion(postcard)
,它的作用是用于路由的有效性的检测,假如是Provider的话它会给传入的postcard
设置对应的服务对象,假如是Fragment则会设为绿色通道。我们看具体的方法源码:
1 | public synchronized static void completion(Postcard postcard) { |
页面跳转之再分析
我们在页面跳转的时候就分析了跳转的基本的流程,我们再看下剩余的疑问,即completion(Postcard postcard)
,其实上面已经回答了这个问题了,我们构建路由,无论是直接使用build
方法,还是直接调用navigation
方法,都会经过completion
,由它去找到路由的注解配置,以及一些RouteType
的参数。
ARouter自带服务
在说拦截器之前,我们看一下ARouter内置的一些服务(IProvider)。
AutowiredService
:参数注入的服务,他也是实现了IPrivder
接口,并且提供了一个autowire(Object instance)
方法,外面的路由组件,比如Activity
,可以通过调用ARouter.getInstance().inject(this)
来实现参数注入,这块具体的后续再说。InterceptorServiceImpl
,拦截器的服务,我们的所有实现的拦截器,最终都是在这里执行拦截的操作的,具体在说拦截器的时候再说。PathReplaceService
,用于跳转之前的路由替换。它的使用场景是比如在线上的时候,我有一个页面出现了一个严重的bug,需要临时跳转到另外一个页面,这种时候就可以用这个路径替换的Service。使用它需要我们自己去实现PathReplaceService
接口,并且使用@Route
去注解,他的path
没有要求,符合上面说的分组的规则就行。它的接口定义是:我们假如是使用1
2
3
4
5
6
7
8public interface PathReplaceService extends IProvider {
// 通过 build(String path) & build(String path, String group, Boolean afterReplace)构建的路由
//会通过这个方法来做一个路由path的校准
String forString(String path);
//通过build(Uri uri)构建的路由会通过下面这个方法进行校准
Uri forUri(Uri uri);
}path[group]
构建的页面的路由则会通过forString
做一个重定向,通过uri
就通过forUri
做重定向。PretreatmentService
,它与PathReplaceService
的区别是,它可以拦截掉跳转,也可以进行参数的补充。它的作用是一个跳转的预处理服务,也需要我们自己实现该接口并且使用@Route
注解。该接口可以修改path,在增加修改参数,理论上修改postcard
的任何东西都可以,在PathReplaceService
之后执行。1
2
3
4
5
6public interface PretreatmentService extends IProvider {
// 预处理
// 返回true则会继续跳转,false则中断跳转
//可以增加修改参数,可以需改路由信息
boolean onPretreatment(Context context, Postcard postcard);
}
拦截器
拦截器的作用是:假如我们在跳转页面的时候,需要对参数进行校验,或者是对当前用户状态进行判断等之后在进行决定是否需要跳转,或者对参数进行补充,这些都是可以的,但是不可以修改路由的路径,路由的目标类。比如我们有这样的一种场景:我们在调整A页面的时候,需要用户是登录的状态,假如不是登录态,则不允许跳转,跳转失败的时候,就返回到登录页面,那么就可以是类似下面的伪代码实现:
1 | @Route(path="module/a") |
这样可以对路由发起最终的调整之前进行拦截,它与PathReplaceService
跟PretreatmentService
是不太一样的,它是已经确认为有效路由了,是在跳转前的最后一步进行拦截,一般只能是要么打断要么继续,无法对路由地址,目标类等进行替换。
对于使用@Interceptor
注解注解并且实现了IInterceptor
接口的类,就是一个ARouter的拦截器了,IInterceptor
有一个priority
属性,是用于确认优先级的,数字越小优先级越高,越早被执行。然后我们看下拦截器的执行的流程,在路由的跳转偏中,我们提到,在路由过程中,执行完LogisticsCenter.completion(postcard)
(有效性检测&一些类型的参数赋值)之后,假如不是绿色通道,则会执行interceptorService.doInterceptions(Postcard postcard, InterceptorCallback callback)
方法。首先,我们看下什么是绿色通道&它的作用是什么:我们使用ARouter进行页面跳转的时候,可以是通过greenChannel()
,比如:
1 | ARouter.getInstance().build("/test/activity2").greenChannel().navigation(this, 666) |
这样进行路由的话就不会经过拦截器,我们的服务(IProvider)的生成,Fragment的生成也是不需要经过拦截器的(内部手动设置为绿色通道)。加入是非绿色通道的,则需要经过拦截器,
1 | protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { |
然后我们就看下interceptorService
,即InterceptorServiceImpl
的实现,InterceptorServiceImpl
也是服务,实现了InterceptorService
,InterceptorService
继承自IProvider
,我们看下它的init
方法
1 | // 该方法再创建InterceptorServiceImpl的时候被调用,InterceptorServiceImpl是在ARouter初始化之后就调用,在`_ARouter.afterInit()`中(非gradle插件方式初始化) |
然后则是执行拦截器的doInterceptions
方法流程:
1 | private static void checkInterceptorsInitStatus() { |
上面就是拦截器的源码解析了。
参数注入
ARouter还提供了参数自动注入的功能,例如:
1 | @Route(path = "/test/activity1", name = "测试用 Activity") |
我们就可以不用手动给age
赋值了,他会在执行完ARouter.getInstance().inject(this)
之后被赋上值,他除了基本类型,还支持序列化,非序列化直接传值(需要我们实现SerializationService
)以及服务(IProvider)的实例传递等。
它的原理与一般都参数注入是大差不差的,通过使用@Autowired
来给需要注入的变量进行注解,
1 | public @interface Autowired { |
通过ARouter的Compile的AutowiredProcessor
,会把使用了@Autowired
注解的类生成它的辅助类,该类会实现ISyringe
接口的,名称为:以注解所在类的SimpleName+$$ARouter$$Autowired
的类
1 | //ISyringe的定义 |
生成的类的demo,可以看到下面的注解生成类,其中注解类是Test1Activity
1 | public class Test1Activity$$ARouter$$Autowired implements ISyringe { |
可以看到,ARouter基本支持所有的类型传值&取值,假如是非序列化的我们就需要实现SerializationService
,
1 | public interface SerializationService extends IProvider { |
与PretreatmentService
,PathReplaceService
等是类似的,都是继承自IProvider
,我们像是服务那样给实现类增加@Route
即可。
关于生成类的原理我们以后说到ARouter的注解的时候再说(即关于如何通过AbstractProcessor
生成这些类)。
路由动态分组
ARouter还支持动态分组的功能:即我们可以不必通过@Route
注解的形式,就可以给某个组件给加入到路由配置中,然后可以别通过Arouter的方式进行调用,这样的灵活性就会增加,我们可以动态去增加/修改/删除路由配置,注意每次添加的所有的动态路由的group需要一致,而且它的名称不支持像@Route
那样设置,只支持从path
取值,即map中的key值,可以看上面的。
1 | ARouter.getInstance().addRouteGroup(new IRouteGroup() { |
addRouteGroup
方法的实现如下:
1 | boolean addRouteGroup(IRouteGroup group) { |
其他
目前主要是还剩下路由注解器 & 使用gradle plugin初始化没分析,这两篇会在后续文章再分析。