ARouter源码分析

初始化

«Singleton»ARouter«Singleton»_ARouter

初始化调用时序

ARouterARouter_ARouter_ARouterLogisticsCenterLogisticsCenterinitinitinitloadRouterMapafterInit

生成路由表

ARouter是通过在编译阶段使用kapt解析Route注解,再通过javapoet来生成路由表相关代码。注解处理器在arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/RouteProcessor.java

比如module-kotlin模块有三个activity,路由分别是:

  • /one/first
  • /one/second
  • /two/test

生成的路由表如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//生成目录位于module-kotlin/build/generated/source/kapt/debug/com/alibaba/android/arouter/routes
public class ARouter$$Root$$modulekotlin implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("one", ARouter$$Group$$one.class);
routes.put("two", ARouter$$Group$$two.class);
}
}

public class ARouter$$Group$$one implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/one/first", RouteMeta.build(RouteType.ACTIVITY, FistActivity.class, "/one/first", "one", null, -1, -2147483648));
atlas.put("/one/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/one/second", "two", null, -1, -2147483648));
}
}

public class ARouter$$Group$$two implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/two/test", RouteMeta.build(RouteType.ACTIVITY, TestActivity.class, "/two/test", "two", null, -1, -2147483648));
}
}

编译自动生成的代码包含一个Root(IRouteRoot)和两个Group(IRouteGroup)。Root是module模块下路由表入口类,使用模块名称命名生成类ARouter$$Root$$modulekotlin。Group是二级路由路径的第一级(/one/first中的one),本例中分别是one和two,分别对应类ARouter$$Group$$oneARouter$$Group$$two,每个Group类下面才是真正的路由。

从代码中可以看出来ARouter$$Root$$modulekotlin会把两个Group对应的class添加到group列表中。那么,com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin#loadInto方法什么时候被执行呢?代码中找不到调用的地方,实际上是在LogisticsCenter#loadRouterMap方法中调用,这个方法会在编译期间动态插入代码。见下一节介绍。

备注:kapt相当于java apt,kapt同时支持kotlin和java代码的注解。kapt生成的代码在module-kotlin/build/generated/source/kapt目录下,apt生成的代码在module-java/build/generated/ap_generated_sources目录下。

ASM代码注入

在ARouter初始化流程中会调用com.alibaba.android.arouter.core.LogisticsCenter#loadRouterMap方法,该方法负责填充Group表。原始代码如下:

1
2
3
4
5
6
7
8
//LogisticsCenter.java
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());
}

方法中没有任何注册逻辑。实际上这个方法会在编译期间通过ASM来修改方法的实现。ASM注入代码后:

1
2
3
4
5
6
7
8
9
10
11
//LogisticsCenter.class
private static void loadRouterMap() {
registerByPlugin = false;
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
}

代码注入在arouter-gradle-plugin插件中实现,代码对应在arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/launch/PluginLaunch.groovy文件中,插件会注册一个RegisterTransform,这个Transform会在*.class转换成dex文件之前运行。

注入的代码是调用register函数,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//LogisticsCenter.java
private static void register(String className) {
if (!TextUtils.isEmpty(className)) {
try {
Class<?> clazz = Class.forName(className);
//1.通过反射调用类构造方法
Object obj = clazz.getConstructor().newInstance();
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, e);
}
}
}

private static void registerRouteRoot(IRouteRoot routeRoot) {
markRegisteredByPlugin();
if (routeRoot != null) {
//2.调用loadInto方法
routeRoot.loadInto(Warehouse.groupsIndex);
}
}

从上述代码可以看到先通过反射方式创建com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin类的实例对象,然后调用它的loadInto方法。最终效果是group被注册到Warehouse.groupsIndex这个map中,相当于下面这段代码:

1
Warehouse.groupsIndex.put("kotlin", ARouter$$Group$$kotlin.class)

备注: 如果期望编译时打印代码注入相关的日志,可以使用下面命令行进行编译

1
./gradlew clean && ./gradlew -i :app:assembleDebug | tee build_log.txt

路由查找

ARouter初始化完成之后,如果没有发生路由跳转,路由表就还是空的,但group表是有数据。当发生路由跳转时,会首先查找路由表,由于是首次跳转,路由表中找不到路由,就会在group表中查找路由对应的group类,把这个group下面的所有路由注册到路由表,然后再进行跳转。当下次再跳转时路由表中就存在对应的路由了。

跳转页面navigation()class Warehouse {// 【group表】static Map<String, Class<? extends IRouteGroup>> groupsIndex= new HashMap<>();// 【路由表】static Map<String, RouteMeta> routes = new HashMap<>();......}在group表中查找group类存在groupyesno反射初始化group classgroup下的所有路由添加到路由表中抛出异常不存在路由表中存在路由?存在路由信息填充Postcard执行拦截和跳转逻辑

我们通过跳转路由名为/kotlin/test的activity来介绍一下源码实现。使用下面方法打开activity:

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

navigation有好几个重载方法,最终都会走到下面的方法:

1
2
3
4
5
6
7
public final class ARouter {
......
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//_ARouter.java
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
......
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);

try {
LogisticsCenter.completion(postcard); //执行上面流程图的逻辑,代码就不贴了
} catch (NoRouteFoundException ex) {
......
}

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() {
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}

@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(postcard, requestCode, callback);
}

return null;
}

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();

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 (0 != flags) {
intent.setFlags(flags);
}

// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(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;
......
}

return null;
}

找到路由/kotlin/test对应的group类后(本例中对应ARouter$$Group$$kotlin.class),就会利用反射调用group类的初始化,然后调用其loadInto方法将路由注册到路由表Warehouse#routes中,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LogisticsCenter {
......
//本例中groupName="kotlin",group=null
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
//利用反射调用group类的构造方法,然后调用loadInto方法
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
//将对应的group类在group表中删除,因为路由注册到路由表后,group类就可以回收了
Warehouse.groupsIndex.remove(groupName);
}

// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
}

参考文章

一文学会Android Gradle Transform基础使用
Android APK编译流程
AOP 利器 ——ASM 基础入门