专栏首页俞其荣的博客ARouter源码解析(二)前言拦截器解析

ARouter源码解析(二)前言拦截器解析

arouter-api version : 1.4.1

前言

前几天对 ARouter 的页面跳转源码进行了分析,趁着今天有空,就讲讲 ARouter 里面的拦截器吧。

ARouter 拦截器的使用方法在这就不多说了,不了解的同学可以去 GitHub 上看看。那就直接进入正题了。

拦截器解析

把视线转移回 ARouter 的 init 方法

public static void init(Application application) {
    if (!hasInit) {
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        hasInit = _ARouter.init(application);
        // 如果初始化完成了
        if (hasInit) {
            _ARouter.afterInit();
        }

        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}

在 init 中,判断了初始化完成后,调用了 _ARouter.afterInit() 来初始化拦截器,跟进代码去看看。

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

发现有个 InterceptorService ,InterceptorService 就是用来控制拦截的服务组件,来看看它的接口是怎么定义的

public interface InterceptorService extends IProvider {

    /**
     * Do interceptions
     */
    void doInterceptions(Postcard postcard, InterceptorCallback callback);
}

之前我们分析过,IProvider 也是可以用 ARouter.getInstance().build("xxx").navigation() 的形式获取的。关键的代码在 LogisticsCenter 的 completion 方法中

/**
 * Completion the postcard by route metas
 *
 * @param postcard Incomplete postcard, should complete by this method.
 */
public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }

    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        // 省略一大串代码
        ...
    } else {
        // 省略一大串代码
        ...

        switch (routeMeta.getType()) {
            case PROVIDER:  // if the route is provider, should find its instance
                // Its provider, so it must implement IProvider
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}

可以看到,如果是 PROVIDER 类型的,就会反射出一个单例对象,并且设置为绿色通道(即不受拦截器的影响)。更详细的代码就不过多介绍了,不理解的同学可以结合着上一篇博客私下回去再看。

所以其实在 afterInit 方法中,只是获取到了 InterceptorService 的实例对象,我们根据上面的 “/arouter/service/interceptor” 可以很轻松的查到,InterceptorService 接口的实现类就是 InterceptorServiceImpl

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
    private static boolean interceptorHasInit;
    private static final Object interceptorInitLock = new Object();

    ...

    @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class<? extends IInterceptor> interceptorClass = entry.getValue();
                        try {
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            iInterceptor.init(context);
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                        }
                    }

                    interceptorHasInit = true;

                    logger.info(TAG, "ARouter interceptors init over.");

                    synchronized (interceptorInitLock) {
                        interceptorInitLock.notifyAll();
                    }
                }
            }
        });
    }

}

我们先来看 InterceptorServiceImpl 的 init 方法。

在 init 方法中,做的主要事情就是遍历所有 IInterceptor class 并创建出对象,调用其 init 方法,完成初始化操作。

初始化完成之后,InterceptorService又是在哪里被使用的呢?

我们在 _ARouter 的 navigation 方法里可以看到它的踪迹:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

    ...

    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            /**
             * Continue process
             *
             * @param postcard route meta
             */
            @Override
            public void onContinue(Postcard postcard) {
                _navigation(context, postcard, requestCode, callback);
            }

            /**
             * Interrupt process, pipeline will be destory when this method called.
             *
             * @param exception Reson of interrupt.
             */
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }

                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    } else {
        return _navigation(context, postcard, requestCode, callback);
    }

    return null;
}

如果不是绿色通道的话,就会启动拦截器去进行拦截。

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
   if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {

       checkInterceptorsInitStatus();
       // 如果拦截器还没有初始化好
       if (!interceptorHasInit) {
           callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
           return;
       }

       LogisticsCenter.executor.execute(new Runnable() {
           @Override
           public void run() {
               CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
               try {
                   _excute(0, interceptorCounter, postcard);
                   // 设置超时时间 默认300s
                   interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                   if (interceptorCounter.getCount() > 0) {    // 如果 count 大于 0 说明是拦截器超时
                       callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                   } else if (null != postcard.getTag()) {    // 说明是某个拦截器中断了,导致整个流程中断
                       callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                   } else { // 否则就通过
                       callback.onContinue(postcard);
                   }
               } catch (Exception e) {
                   callback.onInterrupt(e);
               }
           }
       });
   } else { // 如果没有拦截器 就通过
       callback.onContinue(postcard);
   }
}

/**
* Excute interceptor
*
* @param index    current interceptor index
* @param counter  interceptor counter
* @param postcard routeMeta
*/
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
   if (index < Warehouse.interceptors.size()) {
       IInterceptor iInterceptor = Warehouse.interceptors.get(index);
       iInterceptor.process(postcard, new InterceptorCallback() {
           @Override
           public void onContinue(Postcard postcard) {
               // Last interceptor excute over with no exception.
               counter.countDown();
               // 一个拦截器执行好后,执行下一个
               _excute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
           }

           @Override
           public void onInterrupt(Throwable exception) {
               // Last interceptor excute over with fatal exception.

               postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
               // 如果其中一个拦截器中断的话,就中断整个流程
               counter.cancel();
               // Be attention, maybe the thread in callback has been changed,
               // then the catch block(L207) will be invalid.
               // The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
//                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.
//                        throw new HandlerException(exception.getMessage());
//                    }
           }
       });
   }
}

上面的代码基本上都加了注释了,这里就不再多讲了。

到这里整个 ARouter 拦截器的流程就差不多讲完了,如果还有哪里不懂的地方可以在评论区留言。

再见?

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ARouter源码解析(一)前言ARouter 源码番外

    之前对 ActivityRouter 的源码做了一次分析,相信大家对路由框架已经有一个大概的理解了。

    俞其荣
  • Activity生命周期调用流程

    在前一篇中,我们分析了 startActivity 的整个流程,并且也讲到了何时调用了 onCreate() 。

    俞其荣
  • Android通过URI获取文件路径

    俞其荣
  • 编译时注解(三)Arouter源码讲解

    项目中我们有时需要跨模块startActivity,但是这样需要配置menifest,不方便。这时就需要阿里的一个路由框架Arouter Arouter的使用...

    提莫队长
  • Android项目解耦--路由框架ARouter源码解析

    上一篇文章Android项目解耦--路由框架ARouter的使用讲述了ARouter在项目中的使用,这边文章主要对ARouter的源码进行学习和分析。

    静默加载
  • LeetCode 125 Valid Palindrome

    题目:https://leetcode.com/problems/valid-palindrome/description/

    ShenduCC
  • Java实现的一个简单计算器,有字符分析功能

    需求:实现一个简单的计算器来分析一个简单的表达式字符串。 表达式字符串可能包含括号,+ +或减号,非负整数和空格。 例子:“1 + 1 = 2,(1)“= 1(...

    用户1289394
  • 流程控制-9(上)

    本文目录 前言 一、顺序结构 二、选择结构1-if语句 三、选择结构2-switch语句 前言 1.默认的运行流程 默认情况下,程序的运行流程...

    Python知识大全
  • CountDownLatch的原理

    上次大概说了CountDownLatch的使用,今天说下实现的原理,CountDownLatch的使用效果和Join差不多,实现起来也比较简单。

    付威
  • Influxdb中的Compaction操作

    Plan,PlanLevel,PlanOptimize返回的都是[]CompactionGroup, 它的类型其实是 [][]string, 即一组可以并行执行...

    扫帚的影子

扫码关注云+社区

领取腾讯云代金券