文章目录
前言
我把Arouter分为开发阶段、编译阶段和运行阶段,分别对应的arouter的三个依赖包
一、开发阶段(arouter-annotation)
arouter-annotation主要定义了注解以及相关参数供我们使用
二、编译阶段(arouter-compiler)
arouter-compiler扫描使用注解的代码,并且通过javapoet将其转换为java代码
核心代码在Process文件夹中,对应类分别处理几种类型注解的处理
javapoet是什么?JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。ARouter利用javapoet生成了各种路由文件。这样就可以在运行的时候去扫描路由文件所在的路径,获取路由文件的类名,调用相关方法初始化路由表。
三、运行阶段(arouter-api)
-
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。
四、运行加速(arouter-register)
ARouter的运行阶段是存在缺陷的,缺陷就在于拿到这个Map的过程 我们在使用ARouter时都需要初始化,ARouter所做的即是在初始化时利用反射扫描指定包名下面的所有className,然后再添加map中。我们分析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");
}