ARouter组件化框架原理分析

ARouter组件化框架原理分析

ARouter是阿里巴巴开源的组件化架构框架,能帮助组件化项目中实现不同模块间的跳转,以及AOP面向切面的编程,能对页面跳转的过程进行很好的干预。本文将从源码角度入手,对该框架的原理进行分析。

项目集成时会集成两个Library,也对应了ARouter的两个阶段。arouter-compiler是用于编译期的,而arouter-api是面向运行期的。下面就从这两个阶段开始讲起。

dependencies {
   
    // Replace with the latest version
    compile 'com.alibaba:arouter-api:?'
    annotationProcessor 'com.alibaba:arouter-compiler:?'
    ...
}

编译阶段

ARouter是可以自动注册页面映射关系的,在每个目标页面上使用注解来标注一些参数,比方Path标注其路径。使用注解时会遇到的第一个问题就是需要找到处理注解的时机,如果在运行期处理注解则会大量地运用反射,而这在软件开发中是非常不合适的,因为反射本身就存在性能问题,如果大量地使用反射会严重影响APP的用户体验,而又因为路由框架是非常基础的框架,所以大量使用反射也会使得跳转流程的用户体验非常差。所以ARouter最终使用的方式是在编译期处理被注解的类,这样就可以做到在运行中尽可能不使用反射,这就是注解处理器的作用。

页面注册的整个流程首先通过注解处理器扫出被标注的类文件,然后按照不同种类的源文件进行分类,分别生成固定格式的类文件,命名规则是工程名ARouter+$$ +xxx+$ $ +模块名。可以看出这里面包含了Group、Interceptor、Providers以及Root。这部分完成之后就意味着编译期的工作已经结束了,之后的初始化其实是发生在运行期的,在运行期只需要通过固定的包名来加载映射文件就可以了,因为注解是由开发者自己完成的,所以了解其中的规则,就可以在使用的时候利用相应的规则反向地提取出来。这就是页面自动注册的整个流程。

自动生成的类:

在这里插入图片描述

编译期流程图:

在这里插入图片描述

运行阶段

运行阶段也分为两部分来分析,初始化和页面跳转。

初始化

    // Initialize the SDK
    ARouter.init(mApplication); // As early as possible, it is recommended to initialize in the Application
    //代码流程
    ...
    ARouter.init(mApplication) -> _ARouter.init(application) -> LogisticsCenter.init(mContext, executor)
    ...

沿着代码流程init会走到LogisticsCenter.init(mContext, executor),该函数通过ClassUtils.getFileNameByPackageName遍历包名"com.alibaba.android.arouter.routes"下的所有class存入routerMap中(即把所有编译期自动生成的class读取出来),然后通过for循环把这些class按照不同类型(Root,Interceptors, Providers)进行处理。通过class的路径,利用Class.forName的方式得到class实例,然后调用该class的loadInto方法把该class关联到Warehouse(Warehouse是整个项目的仓库,里面存有所有class的映射关系)的相应的结构体中,这样所有自动生成的映射关系就存储在内存中了,以便于后续查找时使用。

    /**
     * LogisticsCenter init, load all metas in memory. Demand initialization
     */
    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
   
        mContext = context;
        executor = tpe;

        try {
   
            if (registerByPlugin) {
   
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
   
                Set<String> routerMap;

                // It will rebuild router map every times when debuggable.
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
   
                    logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                    // These class was generated by arouter-compiler.
                    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);    // Save new version name when router map update finishes.
                } else {
   
                    logger.info(TAG, "Load router map from cache.");
                    routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
                }

                logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
                startInit = System.currentTimeMillis();

                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
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值