ARouter源码解析

作者: 奔跑吧李博 | 来源:发表于2020-09-14 23:46 被阅读0次

    越来越多的项目引入ARouter库来配合组件化开发,引入ARouter基本上成了项目标配,那么熟悉ARouter源码就变得尤为重要了。

    ARouter的优势:
    • 支持多模块使用,支持组件化开发
    • 使用注解,实现了映射关系自动注册与分布式路由管理
    • 编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
    • 映射关系按组分类、多级管理,按需初始化
    • 灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出异常
    • 自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
    • 支持依赖注入,可单独作为依赖注入框架使用,从而实现跨模块API调用
    • 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
    • 支持获取Fragment
    ARouter源码主要组成部分:
    • annotation: 定义路由表的结构,ARouter路由框架所使用的全部注解,及其相关类。
    • compiler:创建路由表,注解编译处理器,引入“arouter-annotation”,在编译期把注解标注的相关目标类生成映射文件。
    • api: 在运行期加载逻辑构建路由表,并实现路由控制。

    源码分析

    init

    从ARouter的初始化出发开始进入源码:

        fun initARouter() {
            //配置在DEBUG模式下,打印ARouter的日志
            if (BuildConfig.DEBUG) {
                ARouter.openLog()
                ARouter.openDebug()
            }
            ARouter.init(this)
        }
    

    必须在使用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.");
            }
        }
    

    变量hasInit用于保证初始化代码只执行一次。真正去实现初始化是调用了_ARouter.init方法,让_ARouter作实现类。ARouter作为暴露给用户调用的类,真正实现功能是_ARouter,将内部的功能包装在了_ARouter。

    _ARouter中init方法代码:

        protected static synchronized boolean init(Application application) {
            mContext = application;
            LogisticsCenter.init(mContext, executor);
            logger.info(Consts.TAG, "ARouter init success!");
            hasInit = true;
    
            // It's not a good idea.
            // if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            //     application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
            // }
            return true;
        }
    

    调用了LogisticsCenter.init方法,传入线程池executor。

    LogisticsCenter.init方法:

        public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
            mContext = context;
            executor = tpe;
    
            try {
                // These class was generate by arouter-compiler.
                List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
    
                //
                for (String className : classFileNames) {
                    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);
                    }
                }
    
                if (Warehouse.groupsIndex.size() == 0) {
                    logger.error(TAG, "No mapping files were found, check your configuration please!");
                }
    
                if (ARouter.debuggable()) {
                    logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
                }
            } catch (Exception e) {
                throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
            }
        }
    

    ClassUtils.getFileNameByPackageName方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes包下的所有类名,打包成集合返回。

    拿到所有生成类名的集合后,通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中:

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

    看各个加载类的接口:

    public interface IRouteRoot {
        void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
    }
    
    public interface IInterceptorGroup {
        void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
    }
    
    public interface IProviderGroup {
        void loadInto(Map<String, RouteMeta> providers);
    }
    
    public interface IRouteGroup {
        void loadInto(Map<String, RouteMeta> atlas);
    }
    

    IRouteRoot的实现将有@Route注解的module名添加到参数集合中,也就是groupsIndex。
    IInterceptorGroup的实现将@Interceptor注解的类添加到参数集合中,也就是interceptorsIndex中。
    IProviderGroup的实现将继承自IProvider的类添加到参数集合中,也就是providersIndex中。

    init总结:

    init过程就是把所有注解的信息加载内存中,并且完成所有拦截器的初始化。

    getInstance()

    通过new ARouter()创建或者获取ARouter单例。

    build
        public Postcard build(String path) {
            return _ARouter.getInstance().build(path);
        }
    

    然后调用_ARouter中build方法:

        protected Postcard build(String path) {
            if (TextUtils.isEmpty(path)) {
                throw new HandlerException(Consts.TAG + "Parameter is invalid!");
            } else {
                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                if (null != pService) {
                    path = pService.forString(path);
                }
                return build(path, extractGroup(path));
            }
        }
    

    这里出现一个PathReplaceService,它是继承IProvider的接口,它是预留给用户实现路径动态变化功能。extractGroup方法截取路径中的第一段作为分组名。

    build方法会创建并返回一个Postcard(明信片)对象:

        protected Postcard build(String path, String group) {
            if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
                throw new HandlerException(Consts.TAG + "Parameter is invalid!");
            } else {
                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                if (null != pService) {
                    path = pService.forString(path);
                }
                return new Postcard(path, group);
            }
        }
    

    Postcard类部分代码:

    public final class Postcard extends RouteMeta {
        // Base
        private Uri uri;
        private Object tag;             // A tag prepare for some thing wrong.
        private Bundle mBundle;         // Data to transform
        private int flags = -1;         // Flags of route
        private int timeout = 300;      // Navigation timeout, TimeUnit.Second
        private IProvider provider;     // It will be set value, if this postcard was provider.
        private boolean greenChannel;
        private SerializationService serializationService;
    
        // Animation
        private Bundle optionsCompat;    // The transition animation of activity
        private int enterAnim;
        private int exitAnim;
    }
    
    navigation

    跳转语句最后调用的是navigation方法,该方法来自于build返回的Postcard类型的类中。

        public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
            return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
        }
    

    最终调用的是_ARouter中的navigation方法:

        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.
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
    
                if (null != callback) {
                    callback.onLost(postcard);
                } else {    // No callback for this invoke, then we use the global degrade service.
                    DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                    if (null != degradeService) {
                        degradeService.onLost(context, postcard);
                    }
                }
    
                return null;
            }
    
            if (null != callback) {
                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
                     *
                     * @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;
        }
    

    大致可看出LogisticsCenter.completion(postcard);肯定是试图找到跳转的目标,如果找不到则让callback回调onLost,或者交给全局降级策略处理。找到则回调callback的onFound方法。

    Interceptor

    拦截功能是通过ARouter提供的interceptorService实现的。

    public interface InterceptorService extends IProvider {
        void doInterceptions(Postcard postcard, InterceptorCallback callback);
    }
    

    在_ARouter中的实现doInterceptions去做拦截处理:

               interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                    @Override
                    public void onContinue(Postcard postcard) {
                        _navigation(context, postcard, requestCode, callback);
                    }
                    @Override
                    public void onInterrupt(Throwable exception) {
                        if (null != callback) {
                            callback.onInterrupt(postcard);
                        }
                    }
                }
    

    简单来说CountDownLatch可以阻塞一个线程,知道内部计数器为0时,才继续执行阻塞的线程,计数器的初始值通过构造传入,通过调用countDown()方法减少一个计数。

    首先会创建一个与拦截器数量相同的CancelableCountDownLatch初始计数值,每放行一个拦截器就countDown,并交给后一个拦截器,如果拦截则清0计数,并将拦截的Throwable存入postcard的tag字段,interceptorCounter.await();阻塞直到计数归0或者阻塞超时(默认是300秒),最后通过interceptorCounter.getCount()判断是否是超时,还是拦截或者放行。

    可以看到拦截的过程都是在子线程中处理,包括Interceptor的process也是在子线程调用的,因此,如果想要在拦截过程中展示dialog等都需要切换到主线程。

    inject

    使用ARouter进行跳转传参,可以通过@Autowired注解属性来实现。而使用前必须要调用inject来实现自动注入。

    ARouter中inject:

        public void inject(Object thiz) {
            _ARouter.inject(thiz);
        }
    

    _ARouter中inject

        static void inject(Object thiz) {
            AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
            if (null != autowiredService) {
                autowiredService.autowire(thiz);
            }
        }
    

    然后调用了AutowiredService的实现类AutowiredServiceImpl的autowire方法:

    @Route(path = "/arouter/service/autowired")
    public class AutowiredServiceImpl implements AutowiredService {
        private LruCache<String, ISyringe> classCache;
        private List<String> blackList;
    
        @Override
        public void init(Context context) {
            classCache = new LruCache<>(66);
            blackList = new ArrayList<>();
        }
    
        @Override
        public void autowire(Object instance) {
            String className = instance.getClass().getName();
            try {
                if (!blackList.contains(className)) {  
                    //核心功能
                    ISyringe autowiredHelper = classCache.get(className);
                    if (null == autowiredHelper) {  // No cache.
                        autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                    }
                    autowiredHelper.inject(instance);
                    classCache.put(className, autowiredHelper);
                }
            } catch (Exception ex) {
                blackList.add(className);    // This instance need not autowired.
            }
        }
    }
    

    ISyringe是一个接口:

    public interface ISyringe {
        void inject(Object target);
    }
    

    它的实现是apt生成的,每一个带有@Autowired注解的类都会生成一个对应的ISyringe的实现。

    private修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常。navigation的时候我们看到了将Uri参数存入Bundle的过程,然后将Bundle中的数据取出,并赋值给@Autowired注解的属性。

    打完收工。

    参考文章

    可能是最详细的ARouter源码分析
    ARouter源码解析

    相关文章

      网友评论

        本文标题:ARouter源码解析

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