美文网首页
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