美文网首页
ARouter源码分析-拦截器

ARouter源码分析-拦截器

作者: dashingqi | 来源:发表于2020-07-22 22:09 被阅读0次
Android_Banner.jpg

简介

  • 通常我们使用ARouter的拦截器,用于页面跳转时的拦截
  • 比如,我们的某些页面,需要用户登录之后才能进入,这时候我们可以使用拦截器。

基本使用

  • 声明一个类 MyInterceptor implements IInterceptor 实现他的process()方法
  • 在 process()方法里面我们可以做拦截的操作
  • 当然我们还需要ARouter提供的 @Interceptor(priority = 5) 注解 作用于当前我们的自定义注解类上,并未它设置优先级
  • 这个优先级值越小那么它的执行优先级就越高(和存储它的映射关系的数据结构有关系是一个红黑树结构)。
@Interceptor(priority = 5)
public class TestInterceptor implements IInterceptor {
    private static final String TAG = "TestInterceptor";

    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        Log.d(TAG, "process: ----> perform");
        if (postcard.getPath().equals("/interceptor/test")) {
            Log.d(TAG, "TestInterceptor ---> /interceptor/test ");
            callback.onContinue(postcard);
        } else {
            callback.onInterrupt(null);
        }
    }

    @Override
    public void init(Context context) {
        Log.d(TAG, "init: ----> perform");

    }
}
  • 这样当我们进行页面跳转的时候,就会执行拦截器中的 process方法了
  • 是不是觉得很神奇,那么接下来我们就分析下它是怎么个运行原理的

原理分析

  • 其实在之前分析页面跳转的时候,我们就涉及到拦截器的部分,只要是关于页面跳转的 都会去执行项目中的拦截器。
  • 在ARouter进行初始化的时候,当我们初始化成功的时候,有一行代码没有进行分析
 public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            hasInit = _ARouter.init(application);
            
            //------> 分析1:
            if (hasInit) {
                _ARouter.afterInit();
            }

            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
        }
    }
_ARouter # afterInit()
    /**
     * 在初始化的时候,执行这个方法
     * /arouter/service/interceptor 路由路径是作用于 InterceptorServiceImpl
     * InterceptorServiceImpl implements InterceptorService 
     * InterceptorService extends IProvider 
     * InterceptorServiceImpl 是provider类型的
     */
    static void afterInit() {
        // 获取到InterceptorServiceImpl的实例
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }
    // 当我们获取到InterceptorServiceImpl的实例的同时,接着就执行了它的init方法?
    
    // 在补充Postcard数据的时候 也就是 LogisticsCenter #completion()
    
    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();
                            //执行init方法
                            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;
            }
    
InterceptorServiceImpl # init()
@Override
    public void init(final Context context) {
        //线程池中执行
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
            // 那么我们的数据仓库中的interceptorIndex是什么时候填充数据的呢?
            // 我们知道在每次进行页面跳转的时候,都会执行拦截器的操作,所在在ARouter初始化的时候,就会将拦截器加载存储到 Warehouse.interceptorsIndex
            // 可以在LogisticsCenter.init()可以找到
                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();
                            //执行了init方法
                            iInterceptor.init(context);
                            // 将我们获取到的自定义拦截器实例装填到数据仓库的的interceptors的集合中
                            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实例什么时候使用呢?
    // 我们进行页面跳转的时候,在我们进行补充Posrcard数据完之后,有使用过的哟
    // 正常 我们进行页面跳转的时候,不是绿色通道需要去调用InterceptorServiceImpl中的doInterceptions()
    //我们看下代码 _ARouter # 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);
        }
InterceptorServiceImpl # doInterceptions()
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        // 首先判断我们是否有自定义的拦截器
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
            // ------> 分析1:
            checkInterceptorsInitStatus();
            
            // 在init方法中 获取到拦截器并存储到集合中后,将初始化拦截器设置为true了
            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 {
                    // ------> 分析2:
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                        } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                            callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                        } else {
                            callback.onContinue(postcard);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }
分析1: checkInterceptorsInitStatus();
 private static void checkInterceptorsInitStatus() {
        synchronized (interceptorInitLock) {
            while (!interceptorHasInit) {
                try {
                    interceptorInitLock.wait(10 * 1000);
                } catch (InterruptedException e) {
                    throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
                }
            }
        }
    }
    //总结来说就是 如果拦截器没有初始化,就会等待10秒,如果超出10秒就会抛出异常
分析2: _excute(0, interceptorCounter, postcard);
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
        //拿到我们自定义的拦截器
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            //执行自定义拦截器的 process()方法
            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());
//                    }
                }
            });
        }
    }
    // 依次取出拦截器实例,然后调用拦截器的process方法,传入回调接口 InterceptorCallBack
    // 正常的话就会毁掉 onCointinue()方法 然后重新执行 _execute()方法只不过需要将index+1了
  • 到这里我们拦截器的初始化到执行的流程就完事了,也就完成了拦截器的使命了

总结

  • 为什么指定在注解中的 priority属性值 越小会被先执行呢?
// 其实这个跟我们数据仓库中 Warehouse # interceptorIndex有关系 它是一个红黑树结构的集合是按照顺序存储的,key是以priority,value是我们自定义拦截器的Class对象
// 当我们从interceptorIndex取出Class对象的时候,是按照 存储的优先级拿到的

相关文章

网友评论

      本文标题:ARouter源码分析-拦截器

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