源码解读Arouter是如何实现的

Posted 丶笑看退场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码解读Arouter是如何实现的相关的知识,希望对你有一定的参考价值。

原理

我们在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。

源码

探索Android路由框架-ARouter之深挖源码(二)

### _ARouter init()
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
  
  .....
    try {
            long startInit = System.currentTimeMillis();
            //billy.qi modified at 2017-12-06
            //load by plugin first
            loadRouterMap();
            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())).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);
                    }
                }
            }
}
  1. 初始化操作,内部调用LogisiticsCenter init帮我们管理逻辑
  2. 第一次加载,ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);先通过com.alibaba.android.arouter.routes包名,扫描下面包含的所有ClassName,后面对其进行存储Sp。第二次加载就直接从Sp中获取。
  3. 然后遍历、匹配,满足条件的添加到具体的集合中,按照文件的前缀不同,将他们添加到映射表中Warehouse的groupsIndexinterceptorsIndexprovidersIndex
### _ARouter.getInstance().build(path)
  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(path)这个方法,这个方法主要是获取分组名称。切割path字符串,默认为path中第一部分为组名。这就证明了如果我们不自定义分组,默认就是第一个分号的内容。

build方法,最终返回的是一个Postcard对象。

_ARoter navigation(Class<? extends T> service)会调用LogisticsCenter.completion(postcard)

  1. 首先,根据path在Warehouse.routes映射表中查找对应的RouteMeta。但是,第一次加载的时候,是没有数据的。(而第一次加载是在LogisticsCenter.init()中,上面也说了。因此只有WarehousegroupsIndexinterceptorsIndexprovidersIndex 有数据),因此这个时候routeMeta=null。所以,这个时候会先从Warehouse.groupsIndex中取出类名前缀为com.alibaba.android.arouter.routes.ARouter$$Group$$group的文件
  2. 接着,将我们添加@Route注解的类映射到Warehouse.routes中;
  3. 然后将已经加载过的组从Warehouse.groupsIndex中移除,这样也避免了重复添加进Warehouse.routes;注意,这个时候Warehouse.routes已经有值了,所以重新调用本方法(completion(postcard))执行了else代码块。

完成了对Warehouse.providers、Warehouse.routes的赋值。

那么Warehouse.interceptors又是在哪里赋值的呢?

### LogisticsCenre.java

provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;

### InterceptorServiceImpl.java

 public void init(final Context context) {
   .....
      IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            iInterceptor.init(context);
                            Warehouse.interceptors.add(iInterceptor);
 }

ARouter缺陷

ARouter的缺陷就在于拿到这个Map的过程,我们在使用ARouter时都需要初始化,ARouter所做的即是在初始化时利用反射扫描指定包名下面的所有className,然后再添加map

//源码代码,插桩前
private static void loadRouterMap() {
	//registerByPlugin一直被置为false
    registerByPlugin = false;
}
//插桩后反编译代码
private static void loadRouterMap() {
    registerByPlugin = false;
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
    register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
    register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");
}

默认通过扫描dex的方式进行加载,通过gradle插件进行自动注册可以缩短初始化时间,同时解决应用加固导致无法直接访问dex文件,初始化失败的问题

ARouter原理与缺陷解析

以上是关于源码解读Arouter是如何实现的的主要内容,如果未能解决你的问题,请参考以下文章

ARouter源码分析

ARouter源码分析—— 缓存与优化

带你一步一步的解析ARouter 源码

Arouter 源码学习 1

ARouter源码分析—— Provider源码分析

揭秘ARouter路由机制,源码+原理+手写框架