路由表的加载
先从ARouter
初始化开始看,就是这句代码ARouter.init(this)
,看下init
方法做了什么,代码如下
public static void init(Application application) { if (!hasInit) { // 首次执行会到这里 ... // 进一步调用了_ARouter的init方法 hasInit = _ARouter.init(application); if (hasInit) { _ARouter.afterInit(); } ... } }
从代码中可以看到,调用了_ARouter
的init
方法,接着跟下去,代码如下
protected static synchronized boolean init(Application application) { ... // 主要是这句代码 LogisticsCenter.init(mContext, executor); ... return true; }
跟到这里,出现了LogisticsCenter
这个类,这个类是什么呢?根据类的名称翻译过来是 “物流中心” 的意思,继续看下它的init
方法做了什么
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { try { ... // 首先从插件中加载路由表 loadRouterMap(); if (registerByPlugin) { logger.info(TAG, "Load router map by arouter-auto-register plugin."); } else { Set<String> routerMap; if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) { // 调试模式或则是新的版本,会重建路由表 // 这里就是从dex文件中查找“com.alibaba.android.arouter.routes”包下的类,放到map中 routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE); if (!routerMap.isEmpty()) { // routerMap有内容的话,就把内容存到sp中 context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply(); } // 保存新的版本号 PackageUtils.updateVersion(context); } else { // 直接从sp文件中拿路由表,就是前面保存到sp文件中的路由表 routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>())); } // 根据包目录,来实例化不同的对象并调用loadInto方法。 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); } } } ... } }
从上面的代码可以看出,这段代码就是加载路由表的核心代码,上面有注释标出了一些代码的业务逻辑,这里再挑出几个比较难理解的地方重点讲解一下,首先是这句代码loadRouterMap();
,注释上说的是从插件中加载路由表,什么意思呢?就是如果我们想缩短ARouter
初始化的时间,可以用 ARouter 的 Gradle 插件,这个插件能自动加载路由表,这样 ARouter 初始化的时候就不需要读取类的信息,从而缩短初始化时间。
插件的工作原理就是在代码编译的时候,插件会找到
LogisticsCenter
类的loadRouterMap
方法,然后在方法中插入路由相关的代码,这样初始化的时候就不会从dex文件中扫描路由表了。
为了证明我不是胡诌的,这里贴出LogisticsCenter
代码编译后的class文件截图,如下
可以看到通过插件编译后,路由表已经插入到源码中。从源码中可以看出,加载路由表是有两种方式的,第一种就是刚才讲到的通过插件加载,第二种就是通过dex文件加载,通过dex文件加载路由表的方式已经 在上面的源码中进行注释了,简单了解下即可。
再回忆下,看这部分的源码是为了什么,是为了了解ARouter如何加载路由文件的,上面已经通过代码了解了如何加载的路由文件,为了方便理解和记忆这里还是用图来总结一下这部分的内容,如下
好了,路由表的加载原理到这里就结束了,下面开始研究路由表的跳转。
路由表的跳转
还是从官方的demo开路由表的跳转
跟进navigation
方法,发现调用到了下面的代码
继续跟进,最终调用到的是_ARouter
的navigation
方法,如下
现在看下_ARouter
的navigation
方法的代码,如下
这里我把Postcard
给标记出来了,为了更够更好的理解代码的原理,这里很有必要先搞清楚Postcard
是什么。
Postcard是什么
Postcard
翻译过来的意思是明信片,它的作用也和明信片的作用类似,里面保存的都是路由跳转的一些信息,可以看下它的成员变量
每个成员变量的作用,如下表
成员变量 | 释义 |
uri | 统一资源标识符,可以用uri作为路径的跳转 |
tag | 用于在 NavigationCallback 的 interrupt() 方法中获取异常信息 |
mBundle | 调用 withString() 等方法设置要传递给跳转目标的数据时,这个数据就是放在 mBundle 中的 |
flags | 调用 withFlag() 设定 Activity 的启动标志时,这个标志就会赋值给 flags 字段 |
timeout | 拦截器链处理跳转事件是放在 CountDownLatch 中执行的,超时时间默认为 300 秒 |
provider | 当我们实现了自定义服务时,参数注解处理器 AutowiredProcessor 会为各个路径创建一个实现注射器 ISyringe 接口的类,在这个类的 inject() 方法中,调用了 ARouter.getInstance().navigation(XXXService.class) ,当 LogisticsCenter 发现这是一个 Provider 时,就会通过反射创建一个 Provider 实例,然后设置给 Postcard ,再进行跳转。 |
greenChannel | 所谓绿色通道,就是不会被拦截器链处理的通道,自定义服务 IProvider 和 Fragment 就是走的绿色通道 |
serializationService | 当我们调用 withObject() 方法时,ARouter 就会获取我们自己自定义的序列化服务 SerializationService,然后调用该服务的 object2Json() 方法,再把数据转化为 String 放入 bundle 中 |
optionsCompat | 转场动画 |
enterAnim/exitAnim | 进入与退出动画 |
理解了Postcard
的作用后,再看navigation
方法的代码,就比较容易理解了。
接着看navigation
方法的代码,如下
重点需要看的地方,已经标出来了,先看标注1的代码做了什么。
标注1的作用
主要代码如下
这里标出了3处,还是一点点的解释
- 标注1:从routeMeta中取值,设置到postcard属性中,还记的routeMeta是什么吗?就是在路由文件生成的时候生成的路由元数据,忘记的话,可以到前文再看下。
- 标注2:解析Uri中的参数,设置到Bundle里。
- 标注3:主要看绿色框的部分,当类型是PROVIDER和FRAGMENT的的时候,设置postcard的greenChannel。
这个方法的作用,总结起来就是完善postcard对象的属性。
标注2的原理
标注2其实比较简单的,就是判断是否是greenChannel,不是greenChannel的话,就进入拦截器中调用onInterrupt
方法,是greenChannel的话就继续进_navigation(postcard, requestCode, callback)
方法,这个方法的代码如下,
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = postcard.getContext(); switch (postcard.getType()) { case ACTIVITY: // Build intent final Intent intent = new Intent(currentContext, postcard.getDestination()); intent.putExtras(postcard.getExtras()); // Set flags. int flags = postcard.getFlags(); if (0 != flags) { intent.setFlags(flags); } // Non activity, need FLAG_ACTIVITY_NEW_TASK if (!(currentContext instanceof Activity)) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Set Actions String action = postcard.getAction(); if (!TextUtils.isEmpty(action)) { intent.setAction(action); } // Navigation in main looper. runInMainThread(new Runnable() { @Override public void run() { startActivity(requestCode, currentContext, intent, postcard, callback); } }); break; case PROVIDER: return postcard.getProvider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: Class<?> fragmentMeta = postcard.getDestination(); try { Object instance = fragmentMeta.getConstructor().newInstance(); 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; }
这部分代码也很好理解,拿到类型之后,分别对对应的类型做处理是ACTIVITY
是的话最后在主线程中调用startActivity
跳转,是FRAGMENT
的话就利用反射创建出Fragment的实例并返回。
总结
本篇文章首先介绍了ARouter的基本使用,然后整体的看了一下Arouter代码的框架,最后对ARouter的路由跳转功能进行原理分析,文章的主要内容也是对ARouter的跳转进行分析,ARouter的功能还是比较多的,感兴趣的话可以自己阅读源码,详细的了解下ARouter的原理。