我把Arouter分为开发阶段、编译阶段和运行阶段,分别对应的arouter的三个依赖包
arouter-annotation主要定义了注解以及相关参数供我们使用
arouter-compiler扫描使用注解的代码,并且通过javapoet将其转换为java代码
核心代码在Process文件夹中,对应类分别处理几种类型注解的处理
javapoet是什么?JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。ARouter利用javapoet生成了各种路由文件。这样就可以在运行的时候去扫描路由文件所在的路径,获取路由文件的类名,调用相关方法初始化路由表。
ARouter.build(path)构建Postcard
LogisticsCenter
LogisticsCenter.completion(postcard)完善postcard的信息,并且完成了path到activity的转换关系(name与class文件的映射)。
Postcard.navigation()实现跳转
Warehouse 数据仓库,存储映射关系到map中
/** * Storage of route meta and other data. * * @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a> * @version 1.0 * @since 2017/2/23 下午1:39 */ class Warehouse { // Cache route and metas static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); static Map<String, RouteMeta> routes = new HashMap<>(); // Cache provider static Map<Class, IProvider> providers = new HashMap<>(); static Map<String, RouteMeta> providersIndex = new HashMap<>(); // Cache interceptor static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]"); static List<IInterceptor> interceptors = new ArrayList<>(); static void clear() { routes.clear(); groupsIndex.clear(); providers.clear(); providersIndex.clear(); interceptors.clear(); interceptorsIndex.clear(); } }
interceptor的map有些特别,是UniqueKeyTreeMap,其实做的很简单,仅仅是在key(优先级)相同时,抛出指定的异常。因此,记住不要出现优先级相同的interceptor。
我们分析LogisticsCenter.init的源码可知
1.初次打开时会利用ClassUtils.getFileNameByPackageName通过反射来扫描对应包下的所有className
2.在初次扫描后会存储在SharedPreferences中,这样后续就不需要再扫描了,这也是一个优化
以上两个过程都是耗时操作,即是ARouter初次打开时可能会造成慢的原因(二次打开因为有sp缓存所以不会慢)
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { //load by plugin first loadRouterMap(); if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin."); } else { Set<String> routerMap; // It will rebuild router map every times when debuggable. if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { logger.info(TAG, "Run with debug mode or new install, rebuild router map."); // These class was generated by arouter-compiler. //反射扫描对应包 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 { logger.info(TAG, "Load router map from cache."); routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>())); } .... } }
于是arouter提供了arouter-register插件解决这个问题。
其基本原理是字节码插桩,通过gradle生成java文件内容插入到源码中,已解决反射的速度问题
//源码代码,插桩前 private static void loadRouterMap() { //registerByPlugin一直被置为false registerByPlugin = false; } //插桩后反编译代码 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"); }