Android 使用阿里ARouter页面跳转浅析


在工作的项目中使用到了阿里的开源工具Arouter作为页面跳转路由,这里记录一下自己对于该框架使用一次跳转的研究。

首先附上github链接GitHub - alibaba/ARouter: An android router middleware that help app navigating to activities and custom services.,对于Arouter的适用性、优点和用法可以自行了解。

在项目中加入了Arouter跳转的代码后,运行项目,首先你会发现一些清单文件,位置在/build/generated/source/apt/release/com/alibaba/android/arouter/routes/下:


这里的几个类分别实现了不同的接口,IRouteGroup、IInterceptorGroup、IProviderGroup、IRouteRoot,这里我们以页面的跳转分析为主,点开IRouteRoot、IRouteGroup的实现类,你会发现它们都在loadInto方法对传入的容器填充了一些数据,在IRouteRoot的实现类中传了IRouteGroup实现类的类名,在IRouteGroup实现类中传入了一个封装的RouteMeta的对象:


仔细一看每个RouteMeta的参数是不是很熟悉?里面出现了你已配置路径的页面名以及注解@Route中设置的path值:


其实在开发者配置完成后,ARouter会生成这些清单文件,在调用初始化方法init时会去加载这些清单文件,将配置的页面缓存起来,同时你也已经发现在loadinfo方法中体现的调用关系是存在root、group这些概念的。

如果你已经了解过Arouter,一定知道Arouter的一个特点:

映射关系按组分类、多级管理,按需初始化

在这里按组分类、多级管理其实已经体现出来了。

看完了清单文件,我们从Arouter的初始化开始入手:

ARouter.init(application);

往下查看,我们发现其实是调用了

_ARouter.init(application)

最终加载清单文件是LogisticsCenter.init方法中实现,关键代码如下:


首先我们按照正常的rlease版本的流程,进入else的代码块,这里发现routerMap是从数据库里面拿出来的,接来下对routerMap的内容className做循环判断,这里className的三个判断条件其实分别是:


ROUTE_ROOT_PAKCAGE +DOT +SDK_NAME +SEPARATOR +SUFFIX_ROOT:


com.alibaba.android.arouter.routes.ARouter$$Root


ROUTE_ROOT_PAKCAGE +DOT +SDK_NAME +SEPARATOR +SUFFIX_INTERCEPTORS:


com.alibaba.android.arouter.routes.ARouter$$Interceptors


ROUTE_ROOT_PAKCAGE+DOT +SDK_NAME +SEPARATOR +SUFFIX_PROVIDERS:


com.alibaba.android.arouter.routes.ARouter$$Providers


找到了清单文件类之后传入了Warehouse类中的成员变量并表用loadInfo方法,到此初始化已经结束,再进入Warehouse中瞧一瞧:


可见对于页面来说,清单的配置最后被缓存到了WareHouse这个类,页面只缓存到了节点不做下级的加载,这也就是之前说的按需加载,这里只存到了类名并没有把节点全部缓存下来。

在发起一个页面跳转的请求的时,所有的请求都被封装成了一个Postcard对象,我们先来看看这个对象如何生成的,最开始Arouter调用的是build方法:


_Arouter的build方法返回了一个新的Postcar对象:


看看这个PathReplaceService是如何生成的,顺着navigation方法往下看,最后在_Arouter的navigation的方法中返回

这里在buildProvider中会对IRouterProvider的缓存中查询是否注册了对应的provider,仅仅只做页面跳转这里可以不用注册,那么整个方法会抛出异常,在catch块中返回null。那么在build中则会进入build(path, extractGroup(path))方法:


在这个方法中,由上面的分析可知这里的pService为空,所以最简单的情况就是返回一个新的由path和group决定的Postcard。

再进行跳转时,将会调用Postcard的navigation方法,最终是将自己传入了_Arouter的navigation方法中:

protected Object navigation(final Context context,final Postcard postcard,final int requestCode,final NavigationCallback callback) 

在该方法中首先调用了LogisticsCenter.completion(postcard),这方法比较长,一段一段来看,其实这是个注册Postcard的方法:


首先从Root容器中去查找有没有对应的内容,从我们之前的加载过程中看是肯定找不到的,那么第二步则从Group中去找,找到了之后则把页面的内容加载到routes容器中,这里是通过之前init缓存的类名找到对应的Group,然后调用Group的loadInfo方法缓存具体的每个节点,也就是到了要使用的时候再加载具体的节点,loadInfo是最开始清单文件中IRouterGroup实现类的方法,具体的填充过程看一下就知道了,再重新reload。



else代码块里对于已经缓存的内容,则把相应的属性填入Postcard中,也就是把routeMeta转化为Postcard

在最后会对routeMeta的类型做判断,在清单文件类中,new reoutMeta时会将页面的类型传入,所以这里是可以判断的。

在LogisticsCenter.completion(postcard)完成之后接着判断是否走的是绿色通道greenChanel(跳过所有的拦截器):


我们先看跳过拦截器的else内的代码块,接着调用_navigation()方法:


可见跳转的地方就在这里啦,在回到之前是否走绿色通道的逻辑,如果是不跳过拦截器,则会调用interceptorService.doInterceptions(postcard,new InterceptorCallback()),这里interceptorService是一个接口,我们找到它的实现类com.alibaba.android.arouter.core.InterceptorServiceImpl瞧一瞧doInterceptions()方法:


着这个方法中首先会判断是否注册了拦截器被缓存进来,拦截器的缓存也是在init的操作中已经做过的,如果有的话则开启一个线程,这里拦截器相关都是异步的,因为拦截器的操作可能非常耗时,其中_excute()方法是拦截器操作的地方,接着看下去:


根据index依次取出拦截器,并调用process()方法,而当我们定义一个拦截器的时候,最重要的便是重写IInterceptor的process()方法,那么到这里拦截器是如何运作已经一目了然了:

还有一个问题,拦截器是有优先级的,这里又是怎么按照优先级拦截,如果断开后续拦截又是怎么断开的,首先我们看在代码中定义的拦截器,这里使用了注解去定义该拦截器的优先级:


在清单文件中加载拦截器时会将该优先级作为key填充到容器中:

在_excute()方法中从缓存中调用拦截器是从index=0作为索引开始的,如果继续拦截则在continue中使index+1并递归,这样就实现了按照优先级拦截:


在清单文件中拦截器的加载说明Warehouse中interceptorsIndex存储的内容以拦截器的优先级作为key,拦截器的类名作为value,并且interceptorsIndex是一个TreeMap,而拦截器具体的类在使用的时候是从interceptors中加载过来的,这其实是在InterceptorServiceImpl中完成的,在_Arouter完成init之后马上会调用一次afterInit():

interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();

到最后会走到LogisticsCenter.completion()方法,在之前我们发现这个方法最后会对routMeta的类型做判断:


当判断为IProvider接口类型时会调用init的方法,回到InterceptorServiceImpl你会发现这个类实现了InterceptorService接口,而InterceptorService又继承自IProvider接口,那么很明显了,看看InterceptorServiceImpl的init()方法:


Warehouse.interceptors的填充就在这里了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,636评论 0 9
  • ARouter源码解读 以前看优秀的开源项目,看到了页面路由框架ARouter,心想页面路由是个啥东东,于是乎网上...
    陆元伟阅读 461评论 0 1
  • 1.要做一个尽可能流畅的ListView,你平时在工作中如何进行优化的? ①Item布局,层级越少越好,使用hie...
    fozero阅读 641评论 0 0
  • 标签 如果要配置的标签,那么必须要先配置标签,代表的包的概念。 包含的属性 name包的名称,要求是唯一的,管理a...
    偷偷得路过阅读 1,171评论 0 0
  • 周日下午收到了前几日下单的《按自己的意愿过一生》,两小时翻完了这本220页的小书。纵观整本书,最喜欢的要数第一章-...
    水桶君阅读 330评论 1 3