说明

Arouter其实只有三个注解,Autowired处理参数的,Interceptor处理拦截器的,Route处理路由和Service的。

1.关于参数的Autowired的注册和初始化

先说最简单的关于参数的。MainActivity的参数,我们希望生成如下类,然后在MainActivity调用inject方法即可。实现通用接口是方便为了调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity$$ARouter$$Autowired implements ISyringe {

@Override
public void inject(Object target) {
//生成的时候判断被注解的类是Activity还是Fragment,然后这里生成的时候可以直接转换
MainActivity act = (MainActivity) target;
Intent intent = act.getIntent();
Bundle bundle = intent.getExtras();
//这里的key为name也是生成类的时候解析被注解的类得到
act.name = intent.getStringExtra("name");
act.age = intent.getIntExtra("age", 0);
}
}

说明:

包名

需要说明的是,在生成这个类的时候,包名是直接以MainActivity所在的包,这也是MainActivity里面的参数不加访问修饰符也可以访问的原因。默认不写可以在同一个包里面访问。

类名

生成的类的类名是固定的,这里举例是要注入的类名+$$ARouter$$Autowired,这样调用inject才能通过固定类名反射new对象,还是会有反射的,不过反射一次之后会缓存下来。

inject方法的调用

根据固定的包名,类名直接反射调用。当然,反射一次之后会有缓存。

2.关于拦截器的Interceptor的注册和初始化

收集拦截器也比较简单,先定义通用接口,然后收集。

1
2
3
public interface IInterceptorGroup {
void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
}

然后我们希望当前模块所有的被注解的拦截器都能放入map里面去,生成如下类即可。

1
2
3
4
5
6
public class ARouter$$Interceptors$$news implements IInterceptorGroup {
@Override
public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(100, SampleInterceptor.class);
}
}

说明:

包名

固定包名com.alibaba.android.arouter.routes

类名

ARouter$$Interceptors$$+模块名,模块名的获取是在build.gradle里面配置的。

1
2
3
4
5
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
初始化

核心就是初始化了,首先要在sdk里面声明一个interceptorMaps用来存放项目所有的拦截器,然后根据接口写好调用方法。

1
2
3
4
5
6
private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
markRegisteredByPlugin();
if (interceptorGroup != null) {
interceptorGroup.loadInto(interceptorMaps);
}
}

然后使用gradle plugin注册transform,扫描所有的jar包,找到固定的包com.alibaba.android.arouter.routes,以及该包下面所有的继承了指定接口IInterceptorGroup的类。

然后使用gradle plugin生成代码调用,生成代码到指定的类指定的方法里面,大概和下面一样。

1
2
3
4
5
6
private static void loadRouterMap() {
registerByPlugin = false;
//auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerInterceptor(new ARouter$$Interceptors$$news());
}

Arouter是生成代码到com.alibaba.android.arouter.core.LogisticsCenter类,然后方法是上面的方法loadRouterMap
我反编译了LogisticsCenter,如下,他其实是调用了一个通用的register方法,看他注释是牺牲了一点性能来让maindex不至于太大,因为如果直接调用上面的方法registerInterceptor(new ARouter$$Interceptors$$news);的话,会关联到太多的类,很多类都会被打入到maindex里面。
LogisticsCenter

3.关于路由的Route的注册和初始化

路由方面包含了比较多的信息,ARouter把路由分成了几种种类,Activity,Fragment,甚至是模块间通信的IProvider也是用路由注解。

1
2
3
4
5
6
7
8
9
10
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");
}

思路也是一样,先定义通用接口。

1
2
3
public interface IRouteGroup {
void loadInto(Map<String, RouteMeta> atlas);
}

然后收集,所有被Route注解的Activity,Provider,Fragment都会按照不同分组收集到不同的类里面。类似下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ARouter$$Group$$groupa implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/groupa/act2", RouteMeta.build(RouteType.ACTIVITY, TestModule2Activity.class, "/groupa/act2", "groupa", new java.util.HashMap<String, Integer>(){{put("nickName", 8); put("age", 3); }}, -1, -2147483648));
atlas.put("/groupa/bservice", RouteMeta.build(RouteType.PROVIDER, BServiceImpl.class, "/groupa/bservice", "groupa", null, -1, -2147483648));
}
}

public class ARouter$$Group$$groupb implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/groupb/act1", RouteMeta.build(RouteType.ACTIVITY, TestModuleActivity.class, "/groupb/act1", "groupb", null, -1, -2147483648));
atlas.put("/groupb/aservice", RouteMeta.build(RouteType.PROVIDER, AServiceImpl.class, "/groupb/aservice", "groupb", null, -1, -2147483648));
}
}

这里特别需要注意的是路由是按组分开收集的,也就是说一个模块里面的Route可以定义多个分组,会生成多个路由表,分组名是/news/login里面的news,当然换成别的就是别的分组了。这也是为什么不同的模块不能使用同一个分组的原因。

说明:

包名

固定包名com.alibaba.android.arouter.routes

类名

ARouter$$Group$$+分组名,分组名称是在Route注解的参数里面定义的。

初始化

初始化对路由来说是一个需要处理的事情,因为一般我们用工具去查看市面的应用,会发现对应的ActivityInfo大概一般都会在250个以上,并且这几百个路由分散在不同的模块里面了,如果同时去加载这几百个路由,再加上还有的拦截器和Provider,那么启动应用的速度可想而知。
所以Arouter才有分组的概念,对同个模块的路由分组,不同模块的当然分组也不同了,每个模块定义一个分组管理类,然后对该模块分组的所有路由类进行收集,初始化的时候只初始化这个分组类。具体要使用到某个路由,先初始化分组类,再加载该分组的所有路由。
初始化步骤和拦截器和Provider一样。先定义接口,然后收集当前模块的所有分组类进行添加。

1
2
3
public interface IRouteRoot {
void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}

然后收集,所有当前模块的对应的ARouter$$Group$$+分组名类,以分组名为key。

1
2
3
4
5
6
7
public class ARouter$$Root$$news implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("groupb", ARouter$$Group$$groupb.class);
routes.put("groupa", ARouter$$Group$$groupa.class);
}
}

然后是初始化,和拦截器和Provider是一样的。首先要在sdk里面声明一个routerMaps用来存放项目所有的IRouteGroup,然后根据接口写好调用方法。

1
2
3
4
5
6
private static void registerRouteRoot(IRouteRoot routeRoot) {
markRegisteredByPlugin();
if (routeRoot != null) {
routeRoot.loadInto(routerMaps);
}
}

然后使用gradle plugin注册transform,扫描所有的jar包,找到固定的包com.alibaba.android.arouter.routes,以及该包下面所有的继承了指定接口IRouteRoot的类。

然后使用gradle plugin生成代码调用,生成代码到指定的类指定的方法里面,大概和下面一样。

1
2
3
4
5
6
private static void loadRouterMap() {
registerByPlugin = false;
//auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter$$Root$$news());
}

Arouter是生成代码到com.alibaba.android.arouter.core.LogisticsCenter类,然后方法是上面的方法loadRouterMap。具体也是和拦截器一样

关于Provider的说明:
Provider也和路由一样,先定义接口然后收集,当前模块所有的Provider会被收集到一个单独的类里面。
public interface IProviderGroup {
void loadInto(Map<String, RouteMeta> providers);
}
然后收集,所有被Route注解的IProvider的子类都会被收集到。

1
2
3
4
5
6
7
public class ARouter$$Providers$$news implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.module.BService", RouteMeta.build(RouteType.PROVIDER, BServiceImpl.class, "/module/bservice", "groupb", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module.AService", RouteMeta.build(RouteType.PROVIDER, AServiceImpl.class, "/module/aservice", "groupa", null, -1, -2147483648));
}
}

说明:

包名

固定包名com.alibaba.android.arouter.routes

类名

ARouter$$Providerss$$+模块名,模块名的获取是在build.gradle里面配置的。

1
2
3
4
5
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
初始化

这个流程和拦截器一样的,首先要在sdk里面声明一个providerMaps用来存放项目所有的Provider,然后根据接口写好调用方法。

1
2
3
4
5
6
private static void registerProvider(IProviderGroup providerGroup) {
markRegisteredByPlugin();
if (providerGroup != null) {
providerGroup.loadInto(providerMaps);
}
}

然后使用gradle plugin注册transform,扫描所有的jar包,找到固定的包com.alibaba.android.arouter.routes,以及该包下面所有的继承了指定接口IProviderGroup的类。

然后使用gradle plugin生成代码调用,生成代码到指定的类指定的方法里面,大概和下面一样。

1
2
3
4
5
6
private static void loadRouterMap() {
registerByPlugin = false;
//auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerProvider(new ARouter$$Providers$$news());
}

Arouter是生成代码到com.alibaba.android.arouter.core.LogisticsCenter类,然后方法是上面的方法loadRouterMap。具体也是和拦截器一样
LogisticsCenter

4.初始化流程图

Arouter init

5.关于路由的使用

Arouter navigation
这里要说明的是拦截器sdk会自动使用。所以我们一般查找的对象都是RouteMeta对象,我们会在使用一次之后存储下来。

1
ARouter.getInstance().build("/test/activity2").navigation();

首先build会生成Postcard对象,当然这个对象里面只有path即/test/activity2和group即test
然后需要为Postcard填充额外信息,比如类型,是Activity还是Fragment,还是IProvider等其他的参数。当然这些类型之类的参数,我们在使用apt存放的时候都存入进去了。

根据分组名称在routerMaps找到ARouter$$Group$$groupb具体的路由类,反射调用loadInto把当前分组的所有路由都初始化一遍并且缓存起来。同时routerMaps移除这个分组。

然后判断这个类型如果是Provider,反射调用初始化这个Provider,添加到缓存。

最后根据不同的类型返回对象。

还有一直直接通过IProvider对象去构建的,这个会利用到上面生成的providerMaps