原理
我们在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。
源码
探索Android路由框架-ARouter之深挖源码(二)
### _ARouter init()
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
.....
try {
long startInit = System.currentTimeMillis();
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
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);
} 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>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
}
- 初始化操作,内部调用
LogisiticsCenter init 帮我们管理逻辑 - 第一次加载,
ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE); 先通过com.alibaba.android.arouter.routes 包名,扫描下面包含的所有ClassName,后面对其进行存储Sp。第二次加载就直接从Sp中获取。 - 然后遍历、匹配,满足条件的添加到具体的集合中,按照文件的前缀不同,将他们添加到映射表中Warehouse的
groupsIndex 、interceptorsIndex 、providersIndex 中
### _ARouter.getInstance().build(path)
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
根据PathReplaceService 获得预处理路径,这个接口是Iprovider 的子类
extractGroup(path) 这个方法,这个方法主要是获取分组名称。切割path字符串,默认为path中第一部分为组名。这就证明了如果我们不自定义分组,默认就是第一个分号的内容。
build方法,最终返回的是一个Postcard对象。
_ARoter navigation(Class<? extends T> service) 会调用LogisticsCenter.completion(postcard) 。
- 首先,根据path在Warehouse.routes映射表中查找对应的RouteMeta。但是,第一次加载的时候,是没有数据的。(而第一次加载是在LogisticsCenter.init()中,上面也说了。因此只有
Warehouse 的groupsIndex 、interceptorsIndex 、providersIndex 有数据),因此这个时候routeMeta=null。所以,这个时候会先从Warehouse.groupsIndex中取出类名前缀为com.alibaba.android.arouter.routes.ARouter$$Group$$group 的文件 - 接着,将我们添加@Route注解的类映射到Warehouse.routes中;
- 然后将已经加载过的组从
Warehouse.groupsIndex 中移除,这样也避免了重复添加进Warehouse.routes;注意,这个时候Warehouse.routes 已经有值了,所以重新调用本方法(completion(postcard)) 执行了else代码块。
完成了对Warehouse.providers、Warehouse.routes的赋值。
那么Warehouse.interceptors又是在哪里赋值的呢?
### LogisticsCenre.java
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
### InterceptorServiceImpl.java
public void init(final Context context) {
.....
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
}
ARouter缺陷
ARouter 的缺陷就在于拿到这个Map 的过程,我们在使用ARouter 时都需要初始化,ARouter 所做的即是在初始化时利用反射扫描指定包名下面的所有className ,然后再添加map 中
private static void loadRouterMap() {
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");
}
默认通过扫描dex 的方式进行加载,通过gradle 插件进行自动注册可以缩短初始化时间,同时解决应用加固导致无法直接访问dex 文件,初始化失败的问题
ARouter原理与缺陷解析
|