利用 ARouter 实现页面跳转时是会涉及到 ARouter 的 build 方法以及 Postcard 的 navigation 方法。这次先从 build 方法分析一下实现过程。
build 方法内最终会调用 _ARouter 的 navigation(Class extent T> service) 方法,我看比较复杂,所以就细看一下,
protected T navigation(Class extends T> service) {
try{
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
if(null == postcard) {
//这个判断是为了兼容老版本
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
if(null == postcard) {
return null;
}
LogisticsCenter.completion(postcard);
return (T)postcard.getProvider();
} catch (NoRouteFoundException ex) {
return null;
}
}
方法的大概意思是借助入参的 .class 文件,编译生成对应类的对象返回。实现过程是利用 Postcard 类,先创建出 Postcard 类型对象,再通过对 Postcard 对象的 completion,最后从 Postcard 对象里获取对应类的对象实例。
分析过程根据代码大致分三步。
第一步
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
public static Postcard buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);
if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}
这个方法执行需要先了解一个类 Warehouse,字面翻译是仓库,类解释也差不多是这个意思,
//Storage of route meta and other data,用来存储 route 元素和其他数据
class Warehouse {
static Map> groupsIndex = new HashMap<>();
static Map routes = new HashMap<>();
static Map providers = new HashMap<>();
static Map providersIndex = new HashMap<>();
static Map> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List interceptors = new ArrayList<>();
}
看样子主要就是三大部分数据,groupsIndex 和 routes 表示和路径路由相关的数据,providers 和 providersIndex 表示提供器对象一类的数据(这个目前不太懂,先记下),interceptorsIndex 和 interceptors 表示拦截器对象数据。
所以 buildProvider 的工作就是从仓库里找到对应类的 RouteMeta 对象,如果存在就用 RouteMeta 对象信息创建一个 Postcard 对象并返回,没有的话就返回 null,null 的话就表示要么异常,要么不需要。
既然是仓库,那仓库里的 providersIndex 的值是怎么来的?
通过引用查找,发现前面的 LogisticsCenter 初始化用到,ARouter 会事先扫描加载 routes 包下面的类,将类名缓存在一个 Set 集合里。
然后再遍历这个集合,来操作存储指定值到 providersIndex 里,一起执行的还有 groupsIndex 和 interceptorsIndex。
这里有个亮点就是,考虑到从文件里扫描加载 routes 包的内容是个低效率,耗资源的过程,ARouter 采用了 SP 缓存机制,这里有两个缓存,一个是控制要不要重新扫描加载的版本,一个就是包下面的类名集合。
所以在缓存之后,只要 ARouter 版本不改变就不会再次去扫描加载了,因为本身这块也不会频繁变动,也就提高了效率。(但开发阶段除外,其实这个也可以控制是不是开发阶段)
遍历的时候就是去匹配类名里的关键字段,然后应该是用到了反射原理,getConstructor().newInstance() 来创建
ARouter$$Providers$$arouterapi
这个类的对象,并执行 loadInfo() 方法,完成了 providersIndex 赋值。
public void loadInto(Map providers) {
providers.put("com.alibaba.android.arouter.facade.service.AutowiredService", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", (Map)null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.InterceptorService", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", (Map)null, -1, -2147483648));
}
第二步
LogisticsCenter.completion(postcard);
public synchronized static void completion(Postcard postcard) {
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
Class extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
if (null == groupMeta) {
throw new NoRouteFoundException("xxx");
} else {
try{
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
}
completion(postcard);
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) {
Map resultMap = TextUtils.splitQueryParameters(rawUri);
Map paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
for (Map.Entry params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER:
Class extends IProvider> providerMeta = (Class extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try{
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
}
}
postcard.setProvider(instance);
postcard.greenChannel();
break;
case FRAGMENT:
postcard.greenChannel();
break;
default:
break;
}
}
}
这个方法代码比较多,分几个步骤来了解,
- 根据入参 postcard 的 path 路径从仓库的 routes 里获取路由信息
- 判断该路由信息是否存在,如不存在就需要加载,如存在就从路由信息里取出一些数据赋值给 postcard 对象,其中有两个值需要重点关心,uri 和 type。
- 如不存在,会从仓库里的 groupsIndex 里加载组信息,通过组信息创建路由组对象来加载该组的路由信息,然后递归执行 completion 方法。
- 如存在,会重点对 uri 进行参数赋值,具体会利用 Bundle 进行存储参数,接着根据 type 进行操作,具体处理了两种类型 PROVIDER 和 FRAGMENT。如果是 PROVIDER 类型,会创建对应的实例存入仓库中的 providers 里,并且会将该实例赋值给 postcard。
以上就是整个方法的大致逻辑,从上面得出方法的结束肯定是能根据 path 找到对应路由信息,要不然就抛异常了。我们暂且先不具体到代码分析执行,因为这可能涉及到 Java 注解的代码生成,目前还不会,晚点再看。
第三步
return (T)postcard.getProvider();
navigation() 方法的最后一步就是返回 postcard 对象的 provider 对象。