_ARouter 的 navigation(Class service) 方法分析

利用 ARouter 实现页面跳转时是会涉及到 ARouter 的 build 方法以及 Postcard 的 navigation 方法。这次先从 build 方法分析一下实现过程。

build 方法内最终会调用 _ARouter 的 navigation(Class service) 方法,我看比较复杂,所以就细看一下,

protected  T navigation(Class 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 版本不改变就不会再次去扫描加载了,因为本身这块也不会频繁变动,也就提高了效率。(但开发阶段除外,其实这个也可以控制是不是开发阶段)


routes包下面的类.png

遍历的时候就是去匹配类名里的关键字段,然后应该是用到了反射原理,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 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 providerMeta = (Class) 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;
        }
    }
}

这个方法代码比较多,分几个步骤来了解,

  1. 根据入参 postcard 的 path 路径从仓库的 routes 里获取路由信息
  2. 判断该路由信息是否存在,如不存在就需要加载,如存在就从路由信息里取出一些数据赋值给 postcard 对象,其中有两个值需要重点关心,uri 和 type。
  3. 如不存在,会从仓库里的 groupsIndex 里加载组信息,通过组信息创建路由组对象来加载该组的路由信息,然后递归执行 completion 方法。
  4. 如存在,会重点对 uri 进行参数赋值,具体会利用 Bundle 进行存储参数,接着根据 type 进行操作,具体处理了两种类型 PROVIDER 和 FRAGMENT。如果是 PROVIDER 类型,会创建对应的实例存入仓库中的 providers 里,并且会将该实例赋值给 postcard。

以上就是整个方法的大致逻辑,从上面得出方法的结束肯定是能根据 path 找到对应路由信息,要不然就抛异常了。我们暂且先不具体到代码分析执行,因为这可能涉及到 Java 注解的代码生成,目前还不会,晚点再看。

第三步

return (T)postcard.getProvider();

navigation() 方法的最后一步就是返回 postcard 对象的 provider 对象。

你可能感兴趣的:(_ARouter 的 navigation(Class service) 方法分析)