Arouter

作者: 嗯哼嗯哼嗯哼嗯哼 | 来源:发表于2019-12-10 09:59 被阅读0次

    Arouter

    Arouter是一款路由框架,在做组件化是用于组件间通信(包括页面跳转,调用另一个组件的服务等),没分析代码认为Arouter只能用来做页面跳转,代码分析之后发现还有ICO,根据URL查找Class的功能,服务提供等。下面就开始分析Arouter

    //初始化代码
    public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            //进行_Arouter.init()
            hasInit = _ARouter.init(application);
    
            if (hasInit) {
                _ARouter.afterInit();
            }
    
            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
        }
    }
    

    真正进行初始化的是_Arouter类,而Arouter是对_Arouter的功能进行封装,并且Arouter决定需要暴露哪些_Arouter的服务提供给使用者使用

    1. 调用_ARouter.init()进行初始化,真正进行初始化的类是_Arouter
    2. 调用_ARouter.afterInit()进行初始化之后的操作

    下面继续追踪

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

    LogisticsCenter是包含了所有的map的映射,当第一次使用时创建实例,用于处理映射关系,复杂的逻辑去解决重复的组定义,简单来说就是会对处理所有使用@Route注解的类,在处理跳转等操作时会添加完善PostCard,下面详细分析下LogiscCenter

    LogiscCenter

    LogiscCenter有几个重要的方法,我们先看看init()方法

    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        
    
            //如果是debug或者是新版本,就重新加载映射关系,否则从本地获取
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                //加载路由映射,通过指定包名,扫描包下面包含的所有的ClassName,这里的类全是通过APT编译时生成的,这些类包含了所有的映射关系,有Route注解和IProvider服务提供者等注解
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                if (!routerMap.isEmpty()) {
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
    
                PackageUtils.updateVersion(context);    // 更新版本
            } else {
                //非debug模式,也不是新版本,就从本地获取映射关系
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }
    
            
            startInit = System.currentTimeMillis();
            //遍历所有在com.alibaba.android.arouter.routes包下的类,并且做分类,分别放入Warehouse的映射对象内
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 这里是根映射,对应的类为ARouter$$Root$$XXX,生成的所有group类的映射,是对分组的映射
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // 加载拦截器interceptor映射
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // 加载服务提供者Iprovider映射
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
            
          ...
    }
    
    1. 扫描com.alibaba.android.arouter.routes包下所有的Class
    2. 遍历扫描到的Class的集合,如果Class是以"com.alibaba.android.arouter.routes.ARouter$$Root"开头,则这个类里面的映射装载进Warehouse.groupsIndex。这个类里面是生成的各个组映射的Class
    3. 如果Class是以"com.alibaba.android.arouter.routes.ARouter$$Interceptors",则装载进Warehouse.interceptorsIndex拦截器索引映射
    4. 如果Class是以"com.alibaba.android.arouter.routes.ARouter$$Providers",则装载进Warehouse.providersIndex服务提供者索引映射

    到这一步已经把所有的@Route注解和相应的Class装载进了WareHouse里面,完成了路由映射的装载

    下面再看下LogiscCenter的completion()方法,这个方法主要是对路由信息的封装类Postcard的完善,再跳转过程中,通过调用completion()方法,通过RouteMeta完善路由信息

    public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }
    
        //首先查找路由信息是否已经加载到Warehouse.routes映射里面
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {    // 如果为null,那么可能为空,也可能是还没把对应的路由装载进来,Arouter是分组装载,在初始化是只会加载包含分组映射类Class的信息,然后根据分组再加载分组内详细的路由信息
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // 得到包含分组路由信息的类Class的
            if (null == groupMeta) {
            //如果没有对应分组类,则抛出异常
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
    
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    //把包含分组路由信息的映射装载进Warehouse.routes
                    iGroupInstance.loadInto(Warehouse.routes);
                    //移除分组group信息
                    Warehouse.groupsIndex.remove(postcard.getGroup());
    
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
    
                completion(postcard);   // 重新继续完善
            }
        } else {
        //把RouteMeta的相关属性设置给PostCard对象
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());
    
            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // 把Uri携带的参数信息解析出来,放进Bundle对象
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                Map<String, Integer> paramsType = routeMeta.getParamsType();
    
                if (MapUtils.isNotEmpty(paramsType)) {
                    // Set value by its type, just for params which annotation by @Param
                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                        setValue(postcard,
                                params.getValue(),
                                params.getKey(),
                                resultMap.get(params.getKey()));
                    }
    
                    // Save params name which need auto inject.
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }
    
                // Save raw uri
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }
    
            switch (routeMeta.getType()) {
                case PROVIDER:  // 如果@Route的注解类型是服务提供者IProvider
                    // 获取想要获取当前IProvider的Class类型
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    //获取当前类型的实例,如果没有实例的则进行初始化,构造出IProvider
                    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);//把instance设置给postcard
                    postcard.greenChannel();    //provider默认走绿色通道,不用经过拦截器
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment类型也不需要经过拦截器
                default:
                    break;
            }
        }
    }
    
    

    completion()做的主要工作就是找到路由类的RouteMeta对象,来完善Postcard信息

    1. 根据path信息在Warehouse.routes里面查找是否含有相应的RouteMeta信息
    2. 如果没有RouteMeta信息,那么可能是对应的分组路由的映射还没加载进内存,所以就继续查找包含分组信息映射的类,实例化对应的IRouteGroup类,把这个分组的映射关系全部加载进来,然后把Warehouse.groupsIndex移除响应分组类,装载完成后再次调用completion(),完善postcard信息
    3. 如果已经有RouteMeta信息,那么就把对应的RouteMeta的信息设置给postcard,如果uri携带了参数信息,就把参数信息全部装进bundle
    4. 如果获取的类型为PROVIDER或者FRAGMENT,那么就会开启绿色通道,不再走拦截器服务。
    5. 如果PROVIDER类型,对于Provider的理解是服务提供者,主要用于一个组件来向另一个组件提供服务,所以一个Class类型的的服务只有一个实例。首先会从Warehouse.providers里面获取对应的实例,如果没有,那么就初始化,然后放入Warehouse.providers映射里面。最终给postcard设置provider

    下面再看下_ARouter.afterInit();方法

    static void afterInit() {
        // 根据Arouter用法,根据path找实例,拿到InterceptorService
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }
    

    afterInit()主要是在装载完所有的路由映射后执行的操作,获取拦截器服务InterceptorService
    而InterceptorService是一个IProvier,获取拦截器服务InterceptorService的过程用的还是Arouter的方式,下面再看下Arouter获取IProvider的方式,上面的方法最终会调用到_Arouter的navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)方法

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        ...
    
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
    
            if (debuggable()) {
                // Show friendly tips for user.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(mContext, "There's no route matched!\n" +
                                " Path = [" + postcard.getPath() + "]\n" +
                                " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                    }
                });
            }
    
            if (null != callback) {
            //回调onLost()
                callback.onLost(postcard);
            } else {
            //如果没有回调Callback,就会调用查找是否有降级服务,如果有则执行降级
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }
    
            return null;
        }
    
        if (null != callback) {
        //  回调onFound()
            callback.onFound(postcard);
        }
    
    //非绿色通道
        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
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }
    
                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 */
                @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;
    }
    
    1. 调用LogisticsCenter.completion(postcard)把PostCard的相关属性信息全部完善
    2. 非绿色通道,调用拦截器服务interceptorService,如果拦截器全部通过的话,就执行_navigation(),如果拦截器执行拦截,会回调callback.onInterrupt()
    3. 绿色通道,直接调用_navigation()方法

    因为completion()已经在上面介绍过了,拦截器服务的相关方法,放在下面再分析,继续追踪_navigation(),也是路由跳转的最后一步了

    private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //如果传入的context为null,那么就会用Application
        final Context currentContext = null == context ? mContext : context;
    
        switch (postcard.getType()) {
        //如果是activity的路由类型
            case ACTIVITY:
                // 构建Intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                //把bundle设置进intent
                intent.putExtras(postcard.getExtras());
    
                // Set flags.
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                //如果传入的context不是activity,那么启动Activity就会传入Intent.FLAG_ACTIVITY_NEW_TASK创建一个新的栈
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
    
                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }
    
                // 之前说过了拦截器的执行是异步的,这里就切回主线程做startActivity
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });
    
                break;
            case PROVIDER:
            //如果路由类型为Provider,就直接返回IProcier对象
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
            //如果是BOARDCAST,CONTENT_PROVIDER,FRAGMENT类型
                Class fragmentMeta = postcard.getDestination();
                try {
                //实例化对象
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    //如果是Fragment的话,设置传递过来的参数
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }
    
                    return instance;
                } catch (Exception ex) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }
    
        return null;
    }
    
    1. 先查看是否context是否为null,如果为null,则赋值为application
    2. 如果是activity类型,就会构造出intent,把相应的参数等信息全部带上。如果context不是activity会给intent设置Intent.FLAG_ACTIVITY_NEW_TASK,创建新的任务栈。因为拦截器的服务是在一步线程做的,所以最后的startActivity会切回主线程
    3. 如果是IProvider类型,那么就直接返回相应Iprovider
    4. 如果是Fragment类型。会实例化,然后把相关参数设置给fragment,最终返回相应实例

    好了,通过上面的分析,已经把路由的跳转流程分析了一遍。具体的路由信息的装载,编译期对注解的处理等还没说。下面再把拦截器的部分分析一下

    Interceptor

    Arouter中的拦截器是全局,通过拦截器服务启动,下面看下拦截器服务InterceptorServiceImpl
    先看下类继承结构InterceptorServiceImpl implements InterceptorService
    而InterceptorService extends IProvider,所以InterceptorServiceImpl就是一个IProvider。并且IInterceptor extends IProvider,所以拦截器IInterceptor也是单例,即服务提供者

    private static boolean interceptorHasInit;//服务是否初始化完成
    private static final Object interceptorInitLock = new Object();//拦截器初始化锁,保证服务在初始化完成后启动
    

    接着看一下init()方法,由于InterceptorServiceImpl是IProvider,所以会在第一次初始化的时候就会调用init()方法

    @Override
    public void init(final Context context) {
    //调用线程池异步执行任务
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
            //判断拦截器索引Warehouse.interceptorsIndex是否为空,即是否有拦截器
                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);
                            //把初始化好的拦截器添加进拦截器列表Warehouse.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.");
                    
                        //把等待获取interceptorInitLock锁的线程唤醒
                    synchronized (interceptorInitLock) {
                        interceptorInitLock.notifyAll();
                    }
                }
            }
        });
    }
    
    1. 异步执行初始化任务,首先判断拦截索引Warehouse.interceptorsIndex是否为空,即是否有拦截器
    2. 遍历拦截器索引列表,实例化每个拦截器,并调用拦截器的init()方法,然后把初始化好的IInterceptor添加进拦截器列表Warehouse.interceptors
    3. 把interceptorHasInit置为true,唤醒正在等待获取interceptorInitLock锁的线程

    Warehouse.interceptorsIndex里面的数据是在LogisticsCenter的init()方法里面被赋值的

    下面再看看拦截器服务里面的doInterceptions()方法

    @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() {
                //可取消的CountDownLatch,countDown的值为拦截器的数量
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                    //开始执行第一个拦截器的拦截,这个方法会一直调用拦截器列表中下一个拦截器的拦截
                        _excute(0, interceptorCounter, postcard);
                        //interceptorCounter等待,如果超时300s还没有执行完,这个方法也会返回
                        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.
                        //如果tag有值,tag是用来携带拦截过程中抛出的异常
                            callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                        } else {
                        //如果上面的情况都没有,那就是正常的返回,所有的拦截方法都执行完了,就继续执行,回调onContinue
                            callback.onContinue(postcard);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
        //没有拦截器的话,就继续执行,回调onContinue
            callback.onContinue(postcard);
        }
    }
    
    1. 首先阻塞等待拦截器服务是否初始化完成
    2. 初始化完成后,创建CancelableCountDownLatch,countDown的值为拦截器的列表,调用_excute(0, interceptorCounter, postcard);开始执行每一个拦截器的拦截方法。
    3. 如果interceptorCounter.await()返回了,如果interceptorCounter的值还没归0,就代表是超时返回,那么就执行拦截回调,调用callback.onInterrupt()。
    4. 如果interceptorCounter不是超时返回,就代表是正常返回,那么就判断postcard的tag是否有值,如果在拦截过程中需要拦截,那么就需要给tag赋值。所以tag不为null的话,就表示出了异常,需要拦截,执行回调callback.onInterrupt()
    5. 如果tag为null,那么就是正常情况,执行回调callback.onContinue()

    下面在继续分析_excute()方法,分析下拦截器如何一个个往下传的

    //index :在拦截器列表中的索引值
    //CancelableCountDownLatch : 传进来的CountDownLatch,用于在拦截时,快速清零返回
    // postcard : postcard对象,发生拦截时会给postcard的tag字段赋值
    
    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() {
            //如果上一个拦截器执行完了,那么允许接着执行下一个拦截的话,就回调onContinue方法
                @Override
                public void onContinue(Postcard postcard) {
                    //上一个拦截器顺利通过,counter调用countDown把值减一
                    counter.countDown();
                    // 执行下一个拦截器,索引值加1
                    _excute(index + 1, counter, postcard);  
                }
                
                //如果不允许执行下一个拦截器的话,就回调onInterrupt()方法,进行打断拦截
                @Override
                public void onInterrupt(Throwable exception) {
                    //上一个拦截器发生了拦截,给postcard的tag赋值
    
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    //对counter清零,是其立刻返回
                    counter.cancel();
                   
                }
            });
        }
    }
    
    1. 获取对应索引值的拦截器,执行process()方法,并且传入回调InterceptorCallback
    2. 如果拦截器顺利执行,回调onCotinue(),counter减一,继续执行下一个拦截器
    3. 如果拦截器拦截,回调onInterrupt(),给postcard的tag赋值,counter清零,立刻返回结束counter的等待

    拦截器服务的内容页分析完了

    相关文章

      网友评论

          本文标题:Arouter

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