ARouter中Interceptor原理理解(一)

由于项目中经常有这样一个需求,若干界面A、B、C等等会进入下一级界面A’,B’,C’,D’如果已经登录则直接进入,如果未登录便跳转到登录界面,如果从登录界面返回则回到入口,如果登录成功则跳转到对应界面。因为最近在试着使用Android JetPack的navigation来实现一个Activity若干Fragment的App,故想借鉴一下ARouter中的Interceptor实现,自己实现一下拦截器功能。

从源码IInterceptor开始,这是一个接口只有一个process方法,继承自IProvider

public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}

IProvider也是一个接口里面只有一个init方法

public interface IProvider {

    /**
     * Do your init work in this method, it well be call when processor has been load.
     *
     * @param context ctx
     */
    void init(Context context);
}

Ctrl+左键点击init方法查看一下,发现了InterceptorServiceImpl类,是一个接口InterceptorService实现类

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {

InterceptorService也是一个继承自IProvider 接口的接口,有一个 doInterceptions方法,接受两个参数
Postcard和InterceptorCallback这个方法的参数和我们需要实现的IInterceptor接口的process的参数一样,
可以猜测是通过InterceptorServiceImpl 拦截ARouter的navigation然后调用IInterceptor.process来通知的。

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

回到InterceptorServiceImpl 看一下InterceptorServiceImpl.init是在哪里被调用的
发现是在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();
                            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;

继续往上找completion被调用的地方
发现是在_ARouter类中的navigation方法中,这个方法有一个重载,两种不同形式都调用了completion

protected <T> T navigation(Class<? extends T> service) 
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) 

根据参数的不同形式可以简单猜测一下返回泛型的方法对应的是Service、返回Object类型方法对应的是Activity
继续往上看

 /**
     * Launch the navigation by type
     *
     * @param service interface of service
     * @param      return type
     * @return instance of service
     */
    public <T> T navigation(Class<? extends T> service) {
        return _ARouter.getInstance().navigation(service);
    }

    /**
     * Launch the navigation.
     *
     * @param mContext    .
     * @param postcard    .
     * @param requestCode Set for startActivityForResult
     * @param callback    cb
     */
    public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
        return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
    }

感觉和猜想的比较接近,被调用的地方已经是熟悉的ARouter类中了。
点开_ARouter类的代码,发现类最开始的注释中是这么写的ARouter core (Facade patten),
这个类是ARouter的核心,使用了门面模式

简单调用ARouter.getInstance().Build().navigation()发现确实调用的是_ARouter类中Object返回类型的navigation()方法,所以从这个方法开始查看navigation时具体做了什么

  /**
     * Use router navigation.
     *
     * @param context     Activity or null.
     * @param postcard    Route metas
     * @param requestCode RequestCode
     * @param callback    cb
     */
    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);

传入了PretreatmentService.class作为参数调用了泛型返回类型的navigation方法

    /**
     * Launch the navigation by type
     *
     * @param service interface of service
     * @param      return type
     * @return instance of service
     */
    public <T> T navigation(Class<? extends T> service) {
        return _ARouter.getInstance().navigation(service);
    }

看一下PretreatmentService类,

/**
 * Pretreatment service used for check if need navigation.
 *
 * @author zhilong [Contact me.](mailto:[email protected])
 * @version 1.0
 * @since 2019-05-08 11:53
 */
public interface PretreatmentService extends IProvider {
    /**
     * Do something before navigation.
     *
     * @param context  context
     * @param postcard meta
     * @return if need navigation.
     */
    boolean onPretreatment(Context context, Postcard postcard);
}

注释的字面意思是通过这个Service来预处理判断是否需要navigation

返回泛型的Navigation方法中的代码量不多

protected <T> T navigation(Class<? extends T> service) {
        try {
            Postcard postcard = LogisticsCenter.buildProvider(service.getName());

            // Compatible 1.0.5 compiler sdk.
            // Earlier versions did not use the fully qualified name to get the service
            if (null == postcard) {
                // No service, or this service in old version.
                postcard = LogisticsCenter.buildProvider(service.getSimpleName());
            }

            if (null == postcard) {
                return null;
            }

            LogisticsCenter.completion(postcard);
            return (T) postcard.getProvider();
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            return null;
        }
    }

先通过LogisticsCenter.buildProvider(service.getName())方法获得PostCard实例不行的话就通过
postcard = LogisticsCenter.buildProvider(service.getSimpleName())方法来获得一个PostCard实例然后作为参数传给之前看到的LogisticsCenter.completion(postcard)方法
completion方法中会用到RouteMeta实例然后判断是否为null,不为null就对PostCard中的Destination、Type、Priority、Extra、Uri等参数进行设置然后如果是IProvider的话会通过RouteMeta反射生成IProvider对应的实现类,把以实现类的Class作为键,实例作为制保存到Warehouse的静态HashMap中,最后会对传进来的PostCard 实例设置GreenChannel信息,
如果是Fragment,就只设置GreenChannel信息

    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;
            }

WareHourse代码

/**
 * Storage of route meta and other data.
 *
 * @author zhilong Contact me.
 * @version 1.0
 * @since 2017/2/23 下午1:39
 */
class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

所以 PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
这行代码就是对navigation一系列预处理
接下来
LogisticsCenter.completion(postcard);这里的postcard是ARouter.getInstance().Build()产生的这个PostCard
从之前分析知道就是对路由信息的一些处理

然后可以看到不是greenChannel就会拦截

 if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {

大致能够知道了,之所以能够拦截是因为ARouter在navigation之前有先做拦截。
看一眼interceptorService是什么,发现就是InterceptorService接口的实现类的引用
回顾开头看到的,这里的就说明了为什么ARouter路由拦截实现IInterceptor接口就可以了

那么还是得关注一下,实现的IInterceptor实现类是什么时候和_ARouter类关联起来的。
很容易想到的就是在App onCreate方法中我们需要调用的ARouter.init()


    /**
     * 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);

            if (hasInit) {
                _ARouter.afterInit();
            }

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

根据之前说的,ARouter本身使用了门面模式ARouter.init(Application application)实际的处理者是_ARouter,自然看一下_ARouter.init和_ARouter类,_ARouter中有一个DefaultPoolExecutor类型的静态成员变量,它是ThreadPoolExecutor的子类,还有一个静态成员变量Handler、InterceptorService实现类以及_ARouter本身。可见_ARouter是一个单例实现

final class _ARouter {
    static ILogger logger = new DefaultLogger(Consts.TAG);
    private volatile static boolean monitorMode = false;
    private volatile static boolean debuggable = false;
    private volatile static boolean autoInject = false;
    private volatile static _ARouter instance = null;
    private volatile static boolean hasInit = false;
    private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
    private static Handler mHandler;
    private static Context mContext;

    private static InterceptorService interceptorService;

    private _ARouter() {
    }

init方法中传入了Application作为mContext和DefaultPoolExecutor实例executor

protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;
        mHandler = new Handler(Looper.getMainLooper());

        return true;
    }

先看到loadRouteMap()方法

 /**
     * arouter-auto-register plugin will generate code inside this method
     * call this method to register all Routers, Interceptors and Providers
     * @author billy.qi Contact me.
     * @since 2017-12-06
     */
    private static void loadRouterMap() {
        registerByPlugin = false;
        //auto generate register code by gradle plugin: arouter-auto-register
        // looks like below:
        // registerRouteRoot(new ARouter..Root..modulejava());
        // registerRouteRoot(new ARouter..Root..modulekotlin());
    }

可以看到Routers, Interceptors and Providers都是通过gradle脚本自动生成的
然后通过反射把这些信息加载到Warehouse类中:

                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        // This one of root elements, load root.
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // Load providerIndex
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }

然后回到ARouter的init方法,只剩下一个_ARouter.afterInit()方法了

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

继续跟踪可以发现是对interceptorService 信息的一些处理。
这次先简单分析一下,
有空在研究一下InterceptorServiceImpl里的细节以及navigation中的细节、ARouter中的@AutoWired的注入也没有仔细研究。

总结一下,当我们调用navigation的时候ARouter先是执行了 LogisticsCenter.completion(postcard)处理了PostCard的元信息,

然后判断是否GreenChannnel,不是的话会通过Gradle自动注册的InterceptorServiceImpl的doInterceptions方法拦截navigation。InterceptorServiceImpl是在ARouter.init方法中在_ARouter.init 方法中的loadRouterMap()产生的因为是gradle脚本产生的,所以发生在编译阶段。

你可能感兴趣的:(知识点归纳)