初始化 «Singleton» ARouter «Singleton» _ARouter
初始化调用时序
ARouter ARouter _ARouter _ARouter LogisticsCenter LogisticsCenter init init init loadRouterMap afterInit
生成路由表 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 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$$one
和ARouter$$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 private static void loadRouterMap () { registerByPlugin = false ; }
方法中没有任何注册逻辑。实际上这个方法会在编译期间通过ASM 来修改方法的实现。ASM注入代码后:
1 2 3 4 5 6 7 8 9 10 11 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 private static void register (String className) { if (!TextUtils.isEmpty(className)) { try { Class<?> clazz = Class.forName(className); 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 ) { 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类 存在group yes no 反射初始化group class group下的所有路由添加到路由表中 抛出异常 不存在 路由表中存在路由? 存在 路由信息填充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 protected Object navigation (final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { ...... postcard.setContext(null == context ? mContext : context); try { LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { ...... } if (null != callback) { callback.onFound(postcard); } if (!postcard.isGreenChannel()) { 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: final Intent intent = new Intent (currentContext, postcard.getDestination()); intent.putExtras(postcard.getExtras()); int flags = postcard.getFlags(); if (0 != flags) { intent.setFlags(flags); } if (!(currentContext instanceof Activity)) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } String action = postcard.getAction(); if (!TextUtils.isEmpty(action)) { intent.setAction(action); } 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 { ...... public synchronized static void addRouteGroupDynamic (String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (Warehouse.groupsIndex.containsKey(groupName)){ Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes); Warehouse.groupsIndex.remove(groupName); } if (null != group) { group.loadInto(Warehouse.routes); } } }
参考文章 一文学会Android Gradle Transform基础使用 Android APK编译流程 AOP 利器 ——ASM 基础入门