ARouter源码解析(二)

作者: 俞其荣 | 来源:发表于2019-01-02 23:54 被阅读1次

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 拦截器的流程就差不多讲完了,如果还有哪里不懂的地方可以在评论区留言。

再见👋

相关文章

  • 阿里ARouter拦截器使用及源码解析(二)

    阿里ARouter的分析计划 阿里ARouter使用及源码解析(一) 阿里ARouter拦截器使用及源码解析(二)...

  • 路由框架ARouter使用及源码解析(一)

    系列文章: 路由框架ARouter使用及源码解析(一)路由框架ARouter使用及源码解析(二) 一、什么是路由?...

  • 路由框架ARouter使用及源码解析(二)

    系列文章: 路由框架ARouter使用及源码解析(一)路由框架ARouter使用及源码解析(二) 上一篇主要介绍了...

  • ARouter源码解析(二)

    arouter-api version : 1.4.1 前言 前几天对 ARouter 的页面跳转源码进行了分析,...

  • ARouter解析五:IoC与依赖注入

    终于来到了ARouter解析的第五篇了,前面陆陆续续分享了四篇ARouter框架的使用和源码内容: ARouter...

  • ARouter源码解析

    如果应用项目要做模块化, 必然绕不开为了模块间跳转解耦而做的处理, 我们都知道一般都会采用路由模式, 本篇主要解析...

  • ARouter源码解析

    越来越多的项目引入ARouter库来配合组件化开发,引入ARouter基本上成了项目标配,那么熟悉ARouter源...

  • ARouter源码解析

    前言 当我们项目变得庞大的时候 使用单模块 就会变得很臃肿 所以当我们使用组件化架构的时候 有一个需要解决的问题就...

  • ARouter源码解析

    ARouter是阿里巴巴出品,帮助 Android App 进行组件化改造的路由框架,我们项目也使用的是ARout...

  • ARouter源码解析

    1-初始化 ARouter.init()开始,init方法的主要工作就是ARouter实例化,_ARouter的初...

网友评论

    本文标题:ARouter源码解析(二)

    本文链接:https://www.haomeiwen.com/subject/uorxrqtx.html