前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入探究 ARouter

深入探究 ARouter

原创
作者头像
大发明家
发布2022-01-15 22:53:26
7950
发布2022-01-15 22:53:26
举报
文章被收录于专栏:技术博客文章
1.init阶段

我们找到Arouter的入口,也就是初始化的地方:

代码语言:txt
复制
if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
代码语言:txt
复制
    ARouter.openLog();     // 打印日志
代码语言:txt
复制
    ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
代码语言:txt
复制
}
代码语言:txt
复制
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

我们直接看ARouter.init方法

代码语言:txt
复制
    /**
代码语言:txt
复制
     * Init, it must be call before used router.
     */
    public static void init(Application application) {
        if (!hasInit) { //确保只初始化一次
            logger = _ARouter.logger;//日志类
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            hasInit = _ARouter.init(application);
代码语言:txt
复制
            if (hasInit) {
代码语言:txt
复制
                _ARouter.afterInit();
代码语言:txt
复制
            }
代码语言:txt
复制
            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
代码语言:txt
复制
        }
代码语言:txt
复制
    }

接着我们进入实现类看下: 继续看 _ARouter.java 实现

代码语言:txt
复制
    protected static synchronized boolean init(Application application) {
代码语言:txt
复制
        mContext = application;
代码语言:txt
复制
        LogisticsCenter.init(mContext, executor); // 实际初始化的地方
代码语言:txt
复制
        logger.info(Consts.TAG, "ARouter init success!");
代码语言:txt
复制
        hasInit = true;
代码语言:txt
复制
        return true;
代码语言:txt
复制
    }

我们只关注重点,LogisticsCenter.init(mContext, executor);,executor是一个线程池。

主要实现都在 LogisticsCenter.init 方法 中, 继续查看

代码语言:txt
复制
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
代码语言:txt
复制
    mContext = context;
代码语言:txt
复制
    executor = tpe;
代码语言:txt
复制
    ...
代码语言:txt
复制
    Set<String> routerMap;//生成类的类名集合
代码语言:txt
复制
    // 如果是debug模式或者是新版本,从apt生成的包中加载类
代码语言:txt
复制
    if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
代码语言:txt
复制
        // ClassUtils.getFileNameByPackageName 就是根据报名查找对应报名下的类, 就不贴代码了..
代码语言:txt
复制
        routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
代码语言:txt
复制
        if (!routerMap.isEmpty()) {//加入sp缓存
代码语言:txt
复制
            context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
代码语言:txt
复制
        }
代码语言:txt
复制
        PackageUtils.updateVersion(context); //更新版本
代码语言:txt
复制
    } else {//否则从缓存读取类名
代码语言:txt
复制
        routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
代码语言:txt
复制
    }
代码语言:txt
复制
    //判断类型,使用反射实例化对象,并调用方法       // 遍历获取到的 class
代码语言:txt
复制
    for (String className : routerMap) {
代码语言:txt
复制
        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
代码语言:txt
复制
            ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
代码语言:txt
复制
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
代码语言:txt
复制
            ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
代码语言:txt
复制
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
代码语言:txt
复制
            ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
代码语言:txt
复制
        }
代码语言:txt
复制
`ROUTE_ROOT_PAKCAGE`是一个常量:
代码语言:txt
复制
```java
代码语言:txt
复制
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";

ClassUtils.getFileNameByPackageName方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes包下的所有类名,打包成集合返回。可以想象遍历整个dex查找指定类名的工作量有多大,怎么办呢?

就需要arouter-gradle-plugin ASM插桩 来解决这个非常耗费性能问题

可以看到初始化就是查找com.alibaba.android.arouter.routes包下的类, 获取实例并强制转化成IRouteRoot,

IInterceptorGroup, IProviderGroup, 然后调用 loadInto 方法.

通过 demo 的代码查找能看到有

com.alibaba.android.arouter.routes.ARouter$$Root$$app这样的类

代码语言:txt
复制
// ARouter$$Root$$app.java
代码语言:txt
复制
/**
代码语言:txt
复制
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        // 以分组做为 key, 缓存到 routes. 
        // routes 是指向 Warehouse.groupsIndex 的引用
        routes.put("service", ARouter$$Group$$service.class);
        routes.put("test", ARouter$$Group$$test.class);
    }
}

可以看到这是在编译期通过分析注解生成的代码.ARouter$$Group$$service.class也是生成.

代码语言:txt
复制
// ARouter$$Group$$service.java
代码语言:txt
复制
/**
代码语言:txt
复制
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$service implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
        ...
    }
}

到这里可以看到demo代码里定义的HelloServiceImpl.java 等出现了. 其实 ARouter

就是通过分析注解在编译期自动生成了一些关联代码. 另外的 Interceptors, Providers 逻辑上类似.

Interceptors 是注册了声明 Interceptor 注解, 并实现 IInterceptor 接口的类, Providers 是注册了声明

Route 注解, 并实现了 IProvider 接口的类

init流程总结:

初始化工作都做完了, 总结一下 ARouter 会在编译期根据注解声明分析自动生成一些注入代码, 初始化工作主要是把这些注解的对象和对注解的配置缓存到

Warehouse 的静态对象中.

2.跳转
代码语言:txt
复制
// 跳转activity的调用
代码语言:txt
复制
ARouter.getInstance().build("/test/activity2").navigation();
代码语言:txt
复制
// ARouter.java 
代码语言:txt
复制
public Postcard build(String path) {
代码语言:txt
复制
    return _ARouter.getInstance().build(path);
代码语言:txt
复制
}
代码语言:txt
复制
// _ARouter.java
代码语言:txt
复制
// group 默认是传进来的 path 第一部分内容. 例如 path = /test/activity1, group会默认为 test
代码语言:txt
复制
// 如果手动声明的,一定要手动传递, 不然会找不到
代码语言:txt
复制
protected Postcard build(String path, String group) {
代码语言:txt
复制
    return new Postcard(path, group);
代码语言:txt
复制
}

这里就是直接返回了一个 Postcard 对象, 并保存了path, group. Postcard 是继承了 RouteMeta

navigation方法最后都要调用的 _ARouter.java 中, 中间过程省略.我们直接看核心代码

代码语言:txt
复制
// _ARouter.java
代码语言:txt
复制
// postcard 就是前面用build 方法构造的对象实例
代码语言:txt
复制
// requestCode 是区分 startAcitivity 的方式.如果不为-1, 就用startActivityForResult的方式启动
代码语言:txt
复制
// NavigationCallback 是对各种状态的回调. 
代码语言:txt
复制
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    try {
代码语言:txt
复制
        // 验证是否能找到对应的 postcard.path
代码语言:txt
复制
        LogisticsCenter.completion(postcard);
代码语言:txt
复制
    } catch (NoRouteFoundException ex) {
代码语言:txt
复制
        // 如果没找到postcard的配置, 调用onLost回调方法, 或者系统配置的"降级服务"(DegradeService)回调
代码语言:txt
复制
        if (null != callback) {
代码语言:txt
复制
            callback.onLost(postcard);
代码语言:txt
复制
        } else {    
代码语言:txt
复制
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
代码语言:txt
复制
            if (null != degradeService) {
代码语言:txt
复制
                degradeService.onLost(context, postcard);
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return null;
代码语言:txt
复制
    }
代码语言:txt
复制
    ... 
代码语言:txt
复制
 }

navigation调用了LogisticsCenter.completion方法做验证, 我们看下 LogisticsCenter.java

这个方法如何验证 postcard, 然后再继续看navigation方法下面的逻辑

代码语言:txt
复制
// LogisticsCenter.java
代码语言:txt
复制
public synchronized static void completion(Postcard postcard) {
代码语言:txt
复制
    // 通过postcard 的 path 查找对应的 RouteMeta
代码语言:txt
复制
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
代码语言:txt
复制
    if (null == routeMeta) {
代码语言:txt
复制
        // 如果没找到, 可能是还没装载过, 需要根据 group 查找对应的 groups
代码语言:txt
复制
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); 
代码语言:txt
复制
        if (null == groupMeta) {
代码语言:txt
复制
            // 如果没找到, 抛出 NoRouteFoundException 错误方法结束
代码语言:txt
复制
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
代码语言:txt
复制
        } else {
代码语言:txt
复制
            // 返回 IRouteGroup 对象, 并调用 loadInto方法.
代码语言:txt
复制
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
代码语言:txt
复制
            iGroupInstance.loadInto(Warehouse.routes);
代码语言:txt
复制
            // 删除 group 缓存
代码语言:txt
复制
            Warehouse.groupsIndex.remove(postcard.getGroup());
代码语言:txt
复制
            // 重新调用 completion 方法,此时对应的 group 已经缓存完成
代码语言:txt
复制
            completion(postcard);   // Reload
代码语言:txt
复制
        }
代码语言:txt
复制
    } else {
代码语言:txt
复制
        // 可以查找到 routeMeta, copy routeMeta 的原始数据到 postcard 中.
代码语言:txt
复制
        postcard.setDestination(routeMeta.getDestination());
代码语言:txt
复制
        postcard.setType(routeMeta.getType());
代码语言:txt
复制
        postcard.setPriority(routeMeta.getPriority());
代码语言:txt
复制
        postcard.setExtra(routeMeta.getExtra());
代码语言:txt
复制
        switch (routeMeta.getType()) {
代码语言:txt
复制
        case PROVIDER: 
代码语言:txt
复制
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
代码语言:txt
复制
            IProvider instance = Warehouse.providers.get(providerMeta);
代码语言:txt
复制
            // 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
代码语言:txt
复制
            if (null == instance) {
代码语言:txt
复制
                IProvider provider;
代码语言:txt
复制
                try {
代码语言:txt
复制
                    provider = providerMeta.getConstructor().newInstance();
代码语言:txt
复制
                    provider.init(mContext);
代码语言:txt
复制
                    Warehouse.providers.put(providerMeta, provider);
代码语言:txt
复制
                    instance = provider;
代码语言:txt
复制
                } catch (Exception e) {
代码语言:txt
复制
                    throw new HandlerException("Init provider failed! " + e.getMessage());
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
            // 设置一个provider 引用
代码语言:txt
复制
            postcard.setProvider(instance);
代码语言:txt
复制
            // provider 默认设置跳过拦截器
代码语言:txt
复制
            postcard.greenChannel(); 
代码语言:txt
复制
            break;
代码语言:txt
复制
        case FRAGMENT:
代码语言:txt
复制
            // fragment 默认设置跳过拦截器
代码语言:txt
复制
            postcard.greenChannel(); 
代码语言:txt
复制
        default:
代码语言:txt
复制
            break;
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
}

completion方法主要是对 group 做一次懒式加载, 我们继续查看 navigation 方法下面的内容:

代码语言:txt
复制
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    ...
代码语言:txt
复制
    // 执行到这里就是找到了 postcard. 执行对应回调
代码语言:txt
复制
    if (null != callback) {
代码语言:txt
复制
        callback.onFound(postcard);
代码语言:txt
复制
    }
代码语言:txt
复制
    // 如果是"绿色通道", 则直接执行_navigation方法, 目前只有provider, fragment 默认是走绿色通道
代码语言:txt
复制
    if (!postcard.isGreenChannel()) { 
代码语言:txt
复制
        // interceptorService 是 ARouter 配置的默认的拦截服务
代码语言:txt
复制
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
代码语言:txt
复制
            public void onContinue(Postcard postcard) {
代码语言:txt
复制
                _navigation(context, postcard, requestCode, callback);
代码语言:txt
复制
            }
代码语言:txt
复制
            public void onInterrupt(Throwable exception) {
代码语言:txt
复制
                if (null != callback) {
代码语言:txt
复制
                    callback.onInterrupt(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
    } else {
代码语言:txt
复制
        // 绿色通道
代码语言:txt
复制
        return _navigation(context, postcard, requestCode, callback);
代码语言:txt
复制
    }
代码语言:txt
复制
    return null;
代码语言:txt
复制
 }

interceptorService 是 ARouter

配置的默认的拦截器com.alibaba.android.arouter.core.InterceptorServiceImpl.java

代码语言:txt
复制
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
代码语言:txt
复制
    // 用线程池异步执行
代码语言:txt
复制
    LogisticsCenter.executor.execute(new Runnable() {
代码语言:txt
复制
        public void run() {
代码语言:txt
复制
            // 初始化一个同步计数类, 用拦截器的 size
代码语言:txt
复制
            CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
代码语言:txt
复制
            try {
代码语言:txt
复制
                // 执行拦截操作, 看到这猜是递归调用.直到 index 满足条件, 退出递归.
代码语言:txt
复制
                _excute(0, interceptorCounter, postcard);
代码语言:txt
复制
                // 线程等待计数完成, 等待300秒...
代码语言:txt
复制
                interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
代码语言:txt
复制
                // 判断退出等待后的一些状态...
代码语言:txt
复制
                if (interceptorCounter.getCount() > 0) { 
代码语言:txt
复制
                    callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
代码语言:txt
复制
                } else if (null != postcard.getTag()) { 
代码语言:txt
复制
                    callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
代码语言:txt
复制
                } else {
代码语言:txt
复制
                    // 没有问题, 继续执行
代码语言:txt
复制
                    callback.onContinue(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            } catch (Exception e) {
代码语言:txt
复制
                // 中断
代码语言:txt
复制
                callback.onInterrupt(e);
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
    });
代码语言:txt
复制
}

我们继续看看_excute方法

代码语言:txt
复制
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
代码语言:txt
复制
    // 递归退出条件
代码语言:txt
复制
    if (index < Warehouse.interceptors.size()) {
代码语言:txt
复制
        // 获取要执行的拦截器
代码语言:txt
复制
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);
代码语言:txt
复制
        // 执行拦截
代码语言:txt
复制
        iInterceptor.process(postcard, new InterceptorCallback() {
代码语言:txt
复制
            public void onContinue(Postcard postcard) {
代码语言:txt
复制
                // 计数器减1
代码语言:txt
复制
                counter.countDown();
代码语言:txt
复制
                // 继续执行下一个拦截
代码语言:txt
复制
                _excute(index + 1, counter, postcard);  
代码语言:txt
复制
            }
代码语言:txt
复制
            public void onInterrupt(Throwable exception) {
代码语言:txt
复制
                // 当被拦截后退出递归
代码语言:txt
复制
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); 
代码语言:txt
复制
                counter.cancel();
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
    }
代码语言:txt
复制
}

和我们猜测的一样, 一个标准的递归调用, 当所有拦截器执行后(假设都不做拦截), 最后还是要回到_navigation方法

代码语言:txt
复制
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    final Context currentContext = null == context ? mContext : context;
代码语言:txt
复制
    // 我们就先只分析 activity 的逻辑
代码语言:txt
复制
    switch (postcard.getType()) {
代码语言:txt
复制
    case ACTIVITY:
代码语言:txt
复制
        // 初始化 intent, 把参数也添加上
代码语言:txt
复制
        final Intent intent = new Intent(currentContext, postcard.getDestination());
代码语言:txt
复制
        intent.putExtras(postcard.getExtras());
代码语言:txt
复制
        // Set flags.
代码语言:txt
复制
        int flags = postcard.getFlags();
代码语言:txt
复制
        if (-1 != flags) {
代码语言:txt
复制
            intent.setFlags(flags);
代码语言:txt
复制
        } else if (!(currentContext instanceof Activity)) { 
代码语言:txt
复制
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
代码语言:txt
复制
        }
代码语言:txt
复制
         // 在 UI 线程进行 start activity
代码语言:txt
复制
        new Handler(Looper.getMainLooper()).post(new Runnable() {
代码语言:txt
复制
            public void run() {
代码语言:txt
复制
                if (requestCode > 0) {  // Need start for result
代码语言:txt
复制
                    ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
代码语言:txt
复制
                } else {
代码语言:txt
复制
                    ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
代码语言:txt
复制
                }
代码语言:txt
复制
                // 动画设置
代码语言:txt
复制
                if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
代码语言:txt
复制
                    ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
代码语言:txt
复制
                }
代码语言:txt
复制
                if (null != callback) { // Navigation over.
代码语言:txt
复制
                    callback.onArrival(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
        break;
代码语言:txt
复制
    }
代码语言:txt
复制
    return null;
代码语言:txt
复制
}
代码语言:txt
复制
``````java
代码语言:txt
复制
// 跳转activity的调用
代码语言:txt
复制
ARouter.getInstance().build("/test/activity2").navigation();
代码语言:txt
复制
// ARouter.java 
代码语言:txt
复制
public Postcard build(String path) {
代码语言:txt
复制
    return _ARouter.getInstance().build(path);
代码语言:txt
复制
}
代码语言:txt
复制
// _ARouter.java
代码语言:txt
复制
// group 默认是传进来的 path 第一部分内容. 例如 path = /test/activity1, group会默认为 test
代码语言:txt
复制
// 如果手动声明的,一定要手动传递, 不然会找不到
代码语言:txt
复制
protected Postcard build(String path, String group) {
代码语言:txt
复制
    return new Postcard(path, group);
代码语言:txt
复制
}

这里就是直接返回了一个 Postcard 对象, 并保存了path, group. Postcard 是继承了 RouteMeta

navigation方法最后都要调用的 _ARouter.java 中, 中间过程省略.我们直接看核心代码

代码语言:txt
复制
// _ARouter.java
代码语言:txt
复制
// postcard 就是前面用build 方法构造的对象实例
代码语言:txt
复制
// requestCode 是区分 startAcitivity 的方式.如果不为-1, 就用startActivityForResult的方式启动
代码语言:txt
复制
// NavigationCallback 是对各种状态的回调. 
代码语言:txt
复制
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    try {
代码语言:txt
复制
        // 验证是否能找到对应的 postcard.path
代码语言:txt
复制
        LogisticsCenter.completion(postcard);
代码语言:txt
复制
    } catch (NoRouteFoundException ex) {
代码语言:txt
复制
        // 如果没找到postcard的配置, 调用onLost回调方法, 或者系统配置的"降级服务"(DegradeService)回调
代码语言:txt
复制
        if (null != callback) {
代码语言:txt
复制
            callback.onLost(postcard);
代码语言:txt
复制
        } else {    
代码语言:txt
复制
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
代码语言:txt
复制
            if (null != degradeService) {
代码语言:txt
复制
                degradeService.onLost(context, postcard);
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        return null;
代码语言:txt
复制
    }
代码语言:txt
复制
    ... 
代码语言:txt
复制
 }

navigation调用了LogisticsCenter.completion方法做验证, 我们看下 LogisticsCenter.java

这个方法如何验证 postcard, 然后再继续看navigation方法下面的逻辑

代码语言:txt
复制
// LogisticsCenter.java
代码语言:txt
复制
public synchronized static void completion(Postcard postcard) {
代码语言:txt
复制
    // 通过postcard 的 path 查找对应的 RouteMeta
代码语言:txt
复制
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
代码语言:txt
复制
    if (null == routeMeta) {
代码语言:txt
复制
        // 如果没找到, 可能是还没装载过, 需要根据 group 查找对应的 groups
代码语言:txt
复制
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); 
代码语言:txt
复制
        if (null == groupMeta) {
代码语言:txt
复制
            // 如果没找到, 抛出 NoRouteFoundException 错误方法结束
代码语言:txt
复制
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
代码语言:txt
复制
        } else {
代码语言:txt
复制
            // 返回 IRouteGroup 对象, 并调用 loadInto方法.
代码语言:txt
复制
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
代码语言:txt
复制
            iGroupInstance.loadInto(Warehouse.routes);
代码语言:txt
复制
            // 删除 group 缓存
代码语言:txt
复制
            Warehouse.groupsIndex.remove(postcard.getGroup());
代码语言:txt
复制
            // 重新调用 completion 方法,此时对应的 group 已经缓存完成
代码语言:txt
复制
            completion(postcard);   // Reload
代码语言:txt
复制
        }
代码语言:txt
复制
    } else {
代码语言:txt
复制
        // 可以查找到 routeMeta, copy routeMeta 的原始数据到 postcard 中.
代码语言:txt
复制
        postcard.setDestination(routeMeta.getDestination());
代码语言:txt
复制
        postcard.setType(routeMeta.getType());
代码语言:txt
复制
        postcard.setPriority(routeMeta.getPriority());
代码语言:txt
复制
        postcard.setExtra(routeMeta.getExtra());
代码语言:txt
复制
        switch (routeMeta.getType()) {
代码语言:txt
复制
        case PROVIDER: 
代码语言:txt
复制
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
代码语言:txt
复制
            IProvider instance = Warehouse.providers.get(providerMeta);
代码语言:txt
复制
            // 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
代码语言:txt
复制
            if (null == instance) {
代码语言:txt
复制
                IProvider provider;
代码语言:txt
复制
                try {
代码语言:txt
复制
                    provider = providerMeta.getConstructor().newInstance();
代码语言:txt
复制
                    provider.init(mContext);
代码语言:txt
复制
                    Warehouse.providers.put(providerMeta, provider);
代码语言:txt
复制
                    instance = provider;
代码语言:txt
复制
                } catch (Exception e) {
代码语言:txt
复制
                    throw new HandlerException("Init provider failed! " + e.getMessage());
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
            // 设置一个provider 引用
代码语言:txt
复制
            postcard.setProvider(instance);
代码语言:txt
复制
            // provider 默认设置跳过拦截器
代码语言:txt
复制
            postcard.greenChannel(); 
代码语言:txt
复制
            break;
代码语言:txt
复制
        case FRAGMENT:
代码语言:txt
复制
            // fragment 默认设置跳过拦截器
代码语言:txt
复制
            postcard.greenChannel(); 
代码语言:txt
复制
        default:
代码语言:txt
复制
            break;
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
}

completion方法主要是对 group 做一次懒式加载, 我们继续查看 navigation 方法下面的内容:

代码语言:txt
复制
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    ...
代码语言:txt
复制
    // 执行到这里就是找到了 postcard. 执行对应回调
代码语言:txt
复制
    if (null != callback) {
代码语言:txt
复制
        callback.onFound(postcard);
代码语言:txt
复制
    }
代码语言:txt
复制
    // 如果是"绿色通道", 则直接执行_navigation方法, 目前只有provider, fragment 默认是走绿色通道
代码语言:txt
复制
    if (!postcard.isGreenChannel()) { 
代码语言:txt
复制
        // interceptorService 是 ARouter 配置的默认的拦截服务
代码语言:txt
复制
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
代码语言:txt
复制
            public void onContinue(Postcard postcard) {
代码语言:txt
复制
                _navigation(context, postcard, requestCode, callback);
代码语言:txt
复制
            }
代码语言:txt
复制
            public void onInterrupt(Throwable exception) {
代码语言:txt
复制
                if (null != callback) {
代码语言:txt
复制
                    callback.onInterrupt(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
    } else {
代码语言:txt
复制
        // 绿色通道
代码语言:txt
复制
        return _navigation(context, postcard, requestCode, callback);
代码语言:txt
复制
    }
代码语言:txt
复制
    return null;
代码语言:txt
复制
 }

interceptorService 是 ARouter

配置的默认的拦截器com.alibaba.android.arouter.core.InterceptorServiceImpl.java

代码语言:txt
复制
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
代码语言:txt
复制
    // 用线程池异步执行
代码语言:txt
复制
    LogisticsCenter.executor.execute(new Runnable() {
代码语言:txt
复制
        public void run() {
代码语言:txt
复制
            // 初始化一个同步计数类, 用拦截器的 size
代码语言:txt
复制
            CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
代码语言:txt
复制
            try {
代码语言:txt
复制
                // 执行拦截操作, 看到这猜是递归调用.直到 index 满足条件, 退出递归.
代码语言:txt
复制
                _excute(0, interceptorCounter, postcard);
代码语言:txt
复制
                // 线程等待计数完成, 等待300秒...
代码语言:txt
复制
                interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
代码语言:txt
复制
                // 判断退出等待后的一些状态...
代码语言:txt
复制
                if (interceptorCounter.getCount() > 0) { 
代码语言:txt
复制
                    callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
代码语言:txt
复制
                } else if (null != postcard.getTag()) { 
代码语言:txt
复制
                    callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
代码语言:txt
复制
                } else {
代码语言:txt
复制
                    // 没有问题, 继续执行
代码语言:txt
复制
                    callback.onContinue(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            } catch (Exception e) {
代码语言:txt
复制
                // 中断
代码语言:txt
复制
                callback.onInterrupt(e);
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
    });
代码语言:txt
复制
}

我们继续看看_excute方法

代码语言:txt
复制
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
代码语言:txt
复制
    // 递归退出条件
代码语言:txt
复制
    if (index < Warehouse.interceptors.size()) {
代码语言:txt
复制
        // 获取要执行的拦截器
代码语言:txt
复制
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);
代码语言:txt
复制
        // 执行拦截
代码语言:txt
复制
        iInterceptor.process(postcard, new InterceptorCallback() {
代码语言:txt
复制
            public void onContinue(Postcard postcard) {
代码语言:txt
复制
                // 计数器减1
代码语言:txt
复制
                counter.countDown();
代码语言:txt
复制
                // 继续执行下一个拦截
代码语言:txt
复制
                _excute(index + 1, counter, postcard);  
代码语言:txt
复制
            }
代码语言:txt
复制
            public void onInterrupt(Throwable exception) {
代码语言:txt
复制
                // 当被拦截后退出递归
代码语言:txt
复制
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); 
代码语言:txt
复制
                counter.cancel();
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
    }
代码语言:txt
复制
}

和我们猜测的一样, 一个标准的递归调用, 当所有拦截器执行后(假设都不做拦截), 最后还是要回到_navigation方法

代码语言:txt
复制
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
    final Context currentContext = null == context ? mContext : context;
代码语言:txt
复制
    // 我们就先只分析 activity 的逻辑
代码语言:txt
复制
    switch (postcard.getType()) {
代码语言:txt
复制
    case ACTIVITY:
代码语言:txt
复制
        // 初始化 intent, 把参数也添加上
代码语言:txt
复制
        final Intent intent = new Intent(currentContext, postcard.getDestination());
代码语言:txt
复制
        intent.putExtras(postcard.getExtras());
代码语言:txt
复制
        // Set flags.
代码语言:txt
复制
        int flags = postcard.getFlags();
代码语言:txt
复制
        if (-1 != flags) {
代码语言:txt
复制
            intent.setFlags(flags);
代码语言:txt
复制
        } else if (!(currentContext instanceof Activity)) { 
代码语言:txt
复制
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
代码语言:txt
复制
        }
代码语言:txt
复制
         // 在 UI 线程进行 start activity
代码语言:txt
复制
        new Handler(Looper.getMainLooper()).post(new Runnable() {
代码语言:txt
复制
            public void run() {
代码语言:txt
复制
                if (requestCode > 0) {  // Need start for result
代码语言:txt
复制
                    ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
代码语言:txt
复制
                } else {
代码语言:txt
复制
                    ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
代码语言:txt
复制
                }
代码语言:txt
复制
                // 动画设置
代码语言:txt
复制
                if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
代码语言:txt
复制
                    ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
代码语言:txt
复制
                }
代码语言:txt
复制
                if (null != callback) { // Navigation over.
代码语言:txt
复制
                    callback.onArrival(postcard);
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        });
代码语言:txt
复制
        break;
代码语言:txt
复制
    }
代码语言:txt
复制
    return null;
代码语言:txt
复制
}
3.对 IProvider 进行 navigation

主要实现是在LogisticsCenter.completion方法中对IProvider进行了一些分支处理

代码语言:txt
复制
        switch (routeMeta.getType()) {
代码语言:txt
复制
        case PROVIDER: 
代码语言:txt
复制
            // 返回实现IProvider接口的类
代码语言:txt
复制
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
代码语言:txt
复制
            // 在缓存中查找
代码语言:txt
复制
            IProvider instance = Warehouse.providers.get(providerMeta);
代码语言:txt
复制
            // 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
代码语言:txt
复制
            if (null == instance) {
代码语言:txt
复制
                IProvider provider;
代码语言:txt
复制
                try {
代码语言:txt
复制
                    provider = providerMeta.getConstructor().newInstance();
代码语言:txt
复制
                    provider.init(mContext);
代码语言:txt
复制
                    Warehouse.providers.put(providerMeta, provider);
代码语言:txt
复制
                    instance = provider;
代码语言:txt
复制
                } catch (Exception e) {
代码语言:txt
复制
                    throw new HandlerException("Init provider failed! " + e.getMessage());
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
            // 设置一个provider 引用
代码语言:txt
复制
            postcard.setProvider(instance);
代码语言:txt
复制
            // provider 默认设置跳过拦截器
代码语言:txt
复制
            postcard.greenChannel(); 
代码语言:txt
复制
            break;
代码语言:txt
复制
    // 可以看 _navigation 方法就是直接返回在 completion 方法中是设置的引用
代码语言:txt
复制
    private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
代码语言:txt
复制
        final Context currentContext = null == context ? mContext : context;
代码语言:txt
复制
        switch (postcard.getType()) {
代码语言:txt
复制
            case PROVIDER:
代码语言:txt
复制
                return postcard.getProvider();
代码语言:txt
复制
        }
代码语言:txt
复制
        return null;
代码语言:txt
复制
    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.init阶段
  • init流程总结:
  • 2.跳转
    • 3.对 IProvider 进行 navigation
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档