ARouter解析(下) 您所在的位置:网站首页 俄罗斯套娃利用什么原理 ARouter解析(下)

ARouter解析(下)

2023-05-28 12:19| 来源: 网络整理| 查看: 265

前言

在上一篇文章ARouter解析(上) - 编译期原理中,我们了解了ARouter在编译期利用了注解处理器与JavaPoet,对我们声明的Route、拦截器等注解进行了预加载。

讲解的话主要围绕源码+注释展开解说,尽量在每章节的总结处贴一下时序图,保持对流程的理解。

本篇的话依然是带着问题来探索:

编译期解析出来的数据到底怎么使用? 路由跳转的全过程是怎么样的? 1. 初始化过程

首先我们来了解一下ARouter初始化的过程,了解其初始化到底做了什么?

ARouter.init(this)

ARouter的初始化需要执行以上代码,那么我们就顺着源码流程一探究竟

public final class ARouter { private ARouter() { } /** * Init, it must be call before used router. */ public static void init(Application application) { if (!hasInit) { logger = _ARouter.logger; //实际上是调用了_ARouter的init方法 hasInit = _ARouter.init(application); if (hasInit) { _ARouter.afterInit(); } } } public static ARouter getInstance() { if (instance == null) { synchronized (ARouter.class) { if (instance == null) { instance = new ARouter(); } } } return instance; } //...some method }

我们可以看出来,ARouter本身为了保持简洁,具体的流程是交由了_ARouter去执行,ARouter本身不包含具体逻辑,接着我们深入_ARouter的init()中看看

final class _ARouter { private _ARouter() { } 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; } }

我们发现_ARouter最后是调用了LogisticsCenter的init方法进行了初始化,这小子藏得有点深,我们继续跟,

public class LogisticsCenter { public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { mContext = context; executor = tpe; try { //这里判断我们是否使用了ARouter的插件,我们暂时先不讲解这部分,默认不使用,此时进入else逻辑 loadRouterMap(); if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin."); } else { Set routerMap; //这里判断我们的路由文件是否有缓存过与改变,如果缓存过并且没改变的话会读取之前的缓存,否则就重新扫描一次Dex文件,获取我们编译期生成的文件 if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { //如果是首次读取,则走该逻辑 //调用getFileNameByPackageName方法扫描Dex文件,找出编译期生成的文件 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 { //之前读取过,并且生成了缓存,则走该逻辑 routerMap = new HashSet(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet())); } //遍历刚刚扫描出来的文件,一共是三种类型,Root、拦截器、服务三种文件 for (String className : routerMap) { if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) { /* 找出编译期生成的Root文件,并且通过反射初始化,调用其loadInto方法,将路由组文件的Class路径保存到Warehouse类中的groupsIndex中, 需要注意的是,此时路由组文件并未初始化 */ ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) { /* 找出其编译期生成的Interceptor文件,并且将其加载到Warehouse类中的interceptorsIndex中, 需要注意的是,此时拦截器并未初始化 */ ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) { /* 找出其编译期生成的服务文件,并且将其加载到Warehouse类中的providersIndex中, 需要注意的是,此时服务并未初始化 */ ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex); } } } } catch (Exception e) { //... } } //该方法用于扫描Dex文件中ARouter在编译期生成的文件 public static Set getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException { final Set classNames = new HashSet(); //获取全部的Dex文件路径 List paths = getSourcePaths(context); //声明了一个锁,保证新线程扫描完毕后才返回 final CountDownLatch parserCtl = new CountDownLatch(paths.size()); //开始循环匹配Dex文件下的Class文件 for (final String path : paths) { DefaultPoolExecutor.getInstance().execute(new Runnable() { @Override public void run() { DexFile dexfile = null; try { //加载Dex文件 if (path.endsWith(EXTRACTED_SUFFIX)) { //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache" dexfile = DexFile.loadDex(path, path + ".tmp", 0); } else { dexfile = new DexFile(path); } //扫描Dex文件 Enumeration dexEntries = dexfile.entries(); while (dexEntries.hasMoreElements()) { String className = dexEntries.nextElement(); //如果符合ARouter编译期生成的Java文件,则将该文件保存到Set集合中 if (className.startsWith(packageName)) { classNames.add(className); } } } catch (Throwable ignore) { Log.e("ARouter", "Scan map file in dex files made error.", ignore); } finally { if (null != dexfile) { try { dexfile.close(); } catch (Throwable ignore) { } } parserCtl.countDown(); } } }); } //该锁会等待线程的遍历完成 parserCtl.await(); //返回编译期文件的全类名 return classNames; } }

我们可以通过阅读注释获知,在LogisticsCenter的init方法中,他主要的初始化工作是扫描全部Dex文件,获得Root、拦截器、服务三个在编译期中生成的类文件,最后保存到Warehouse中。

接下来我们看看Warehouse的样子是什么,他有什么作用?

class Warehouse { //缓存编译期保存在Root文件中的路由组class文件(此时未初始化) static Map destination; // 路由目的所对应的类型,比如XXXActivity private String path; // 路径 private String group; // 路由所在的组 private int priority = -1; // 优先级 private int extra; // 32位的参数 private Map paramsType; // 传递参数 private String name; //名称 }

Postcard就是明信片,它继承至RouteMeta,它的作用就是知道该次路由的目的地与一些路由的信息,同时在初始化的时候,我们知道ARouter有物流中心,有仓库,那么明信片就是决定这次路由的目的地与携带的一些额外信息。

在概念上理解Postcard之后,我们接着看ARouter是怎么初始化它的,我们从ARouter的build方法开始分析

//果不其然,ARouter是调用了_ARouter的方法 public Postcard build(String path) { return _ARouter.getInstance().build(path); }

我们继续跟

final class _ARouter { //1.该方法会给予一次路径处理的timming,我们可以实现PathReplaceService进行干预,接着会继续调用build方法 protected Postcard build(String path) { if (TextUtils.isEmpty(path)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!"); } else { //ARouter在这里提供了第一次路由干预手段,我们可以在ARouter中实现PathReplaceService,对路径进行处理 PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } //调用extractGroup方法获取路径在的Group Name return build(path, extractGroup(path), true); } } } //2.一般在这里就不会触发第二次路径处理,直接new了一个PostCard出来,把路径和组名传递进去 protected Postcard build(String path, String group, Boolean afterReplace) { if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!"); } else { if (!afterReplace) { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } } return new Postcard(path, group); } }

那build也没做什么事情呀,但是PostCard目前只是初始化了path和group,我们刚刚看到的其他信息都没进行初始化,那么一切秘密都藏在Postcard的navigation流程中。

public final class Postcard extends RouteMeta { public Object navigation() { return navigation(null); } public Object navigation(Context context) { return navigation(context, null); } public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); } }

最后我们发现Postcard会将自己传递给ARouter的navigation方法,真是跳来跳去的。没事,我们继续跟!狠狠跟!

final class _ARouter { protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { //ARouter在这里提供第二次干预路由的timming,我们可以PretreatmentService来对Postcard进行修改 PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class); if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) { // Pretreatment failed, navigation canceled. return null; } //为Postcard设置context,这里有可能是application,如果是application在跳转Activity的时候,会自动标记NEW_TASK postcard.setContext(null == context ? mContext : context); try { //之前构建的postCard信息尚未完整,所以需要通过物流中心在仓库中寻找该Postcard的信息,并且进行完善,那么接着我们看看物流中心是怎么完善postCard的信息的 LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { //...暂时省略后续流程,我们先关注 LogisticsCenter.completion方法 } }

我们先看看LogisticsCenter的completion方法

public synchronized static void completion(Postcard postcard) { /* 1. ARouter会先从Warehouse中的routes获得路由信息,但是之前说了,Warehouse,routers只在init中初始化了路由组class, 但是还没有初始化路由组信息所以在第一次的时候这里是不可能获取到任何对象,所以这里会进入if逻辑里 */ RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if (null == routeMeta) { try { //2.该方法正式初始化路由组里面的路由信息,我们可以往下滑,先看addRouteGroupDynamic方法具体实现。 addRouteGroupDynamic(postcard.getGroup(), null); } catch (Exception e) { throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]"); } //4.在初始化路由组后,我们重新加载一下,因为此时Warehouse的routesMap中会含有我们目标路由的RouteMeta数据,此时会进入 completion(postcard); // Reload } else { //5.将仓库之前保存的信息取出,并且保存到postcard中 postcard.setDestination(routeMeta.getDestination()); postcard.setType(routeMeta.getType()); postcard.setPriority(routeMeta.getPriority()); postcard.setExtra(routeMeta.getExtra()); //...暂时忽略uri相关逻辑 switch (routeMeta.getType()) { case PROVIDER: //6.如果该路由最后获取的是服务, Class


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有