Skip to content

Commit 8e9c496

Browse files
update
1 parent 8653d36 commit 8e9c496

3 files changed

Lines changed: 11 additions & 7 deletions

File tree

22.1 KB
Binary file not shown.

源码分析/ARouter/Android 平台页面路由框架 ARouter.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,16 @@ ARouter 大致有以下 7 个优势:
143143

144144
分享的第一个最佳实践就是页面跳转。大家可能提出这样的问题:如果我们在使用 ARouter 这样的路由框架的时候,将每一个目标页面都通过一定的规则注解上如图所示的 Path,在任何场景下都通过 Path 跳转,会不会出现在写代码的时候完全不知道要跳转到哪里,也不知道当前页面会从哪些页面跳进来的问题。其实这样的问题在编程实现的时候对于开发者而言是非常难受的,这也是无耦合所带来的代价,但是其实也可以简单地通过类似于语法糖的写法解决这样的问题。其实在进行了组件化之后,在写代码时也不是所有的页面都需要 Route 进行跳转的,但是在最终实现上却希望所有的页面都通过 Route 进行管理。为了实现这样的目标,其实只需要在目标页面上放一个静态的 launch(这里的 launch 可以换成任何一个你喜欢的方法名字),然后在这个 launch 方法中调用 Route 跳转到当前页面,这样在无法耦合到当前类的时候可以直接使用 ARouter 的 API 并通过 Path 的方式跳转进来。在可以依赖到这个类的场景下,可以直接调用这个类的静态方法跳转到这个页面,这样就解决了我们在日常开发中同一个模块之间的跳转还需要使用 Route 的非常尴尬的情况,而且这样也可以最终实现所有的页面都被 Route 管理,但是看起来并非所有的跳转都需要通过 Route,这样至少在开发中是非常舒服的。
145145

146-
**从外部导航到内部页面**
146+
## 从外部导航到内部页面
147+
147148
接下来要分享的也是路由框架的一个非常重要的功能:从外部导航到内部页面。可以看到下图中的两个截图分别是使用了自定义的 Scheme,另一张图则是使用了原声的 HTTPS 的 Scheme。
148149

149150
![](images/465248067b1cc4ce8267a5784b58de6e1a64d548.png)
150151

151152
对于这些 URL 进行逐段分析,在 Scheme 后面的就是域名,再之后就是 test/activity1,这部分就是真实的页面上所标注的注解,也就是需要将这一行 URL 映射到标注了 test/activity1 的页面上。当然我们可以想到之前使用隐式 intent 也可以做的很好,但是隐式 intent 却存在着很多的局限性,而且无法将参数也注入进去。可以看到 URL 中 “?” 之后就是参数,通过 ARouter 这样的路由框架不但可以跳转到目标页面也可以将后面的一些 Get 参数注入到目标页面的对应字段中。
152153

154+
> 这和 DeepLink 有啥区别吗?
155+
153156
接下来具体分享这部分是如何实现的,首先需要在 APP 的 Manifest 声明一个 activity,但是是这个 activity 不需要页面,只需要注册一个 intent-filter 就可以了。这个 intent-filter 就是用于监听刚刚生成的 Scheme 的,而且 Scheme 可以换成任何想要的,比如 HTTP 或者 HTTPS,也可以使用自定义 Scheme。为什么说这里是一个最佳实践呢,其实通常情况下使用隐式 intent 的时候,每一个从外面跳转进来的页面都需要注册上 intent-filter,每个页面都需要设置 export=true,也就是需要让每一个页面都可以导出,在外部可以访问到。但是这样做会带来非常严重的安全风险,就像是一个房子有十个门还是只有一个门,看门的成本是不同的。而现在使用的这种场景只需要对外暴露出一个 activity,然后在这个 activity 中注册一个 intent-filter,这样之后所有的外部路由请求都会经过这唯一的门,然后在这个 activity 中获取到 URL 并将其交给 ARouter,剩下的就由路由框架做分发了。
154157

155158
![](images/1c04ffeca1b11fe69264f21c5b8c660e6d17afe4.png)
@@ -162,13 +165,13 @@ ARouter 大致有以下 7 个优势:
162165

163166
![](images/e0b2e0176b6e79256049ff392734d577fb357029.png)
164167

165-
**处理登录逻辑 : 拦截器的运用**
168+
## **处理登录逻辑 : 拦截器的运用**
166169

167170
以上分享的就是如何从外部的 URL 跳转到内部的页面并解析参数,接下来分享如何处理登录逻辑。登录逻辑是每个 APP 都会有的功能,有的 APP 是只要用户进入就需要登录的,也有的 APP 是对于一些页面需要登录,另外一些页面也不需要登录,而对于后面的这种 APP 而言,在每个页面中都需要判断是否用户登录了则是非常不合适的做法,这也是最开始考虑到系统原生的路由方案不支持在系统中插入自定义跳转逻辑的比较坑的状况。所以假如使用 ARouter,就能够使用 ARouter 所提供的拦截器的机制解决登录问题。使用 ARouter 解决登录逻辑只需要实现登录拦截器就可以了,不需要在每一个页面都判断是不是需要登录,而只需要在登录拦截器中进行判断。登录拦截器会作用在所有的跳转之间,假设从来源页面跳转到下面的 A、B、C 和 D 这四个目标页面,可以看到图中绿色的是不需要登录页面的,可以直接跳转进入,也就是如绿色的箭头展示的一样是可以直接放行的;而对于 C 页面而言,则属于需要登录的页面,这时就会被拦截器拦截并直接导航到登录页,在用户完成登录或者取消登录后,通过回调或者广播等形式回到拦截器,然后根据从拦截器中得到的结果判断可以直接往下跳转还是终止本次跳转流程,每一个拦截器中都有一个回调,这个回调可以终止本次路由过程也允许直接放行。这就是典型的面向切面编程,当然登录拦截器只是诸多拦截器之一,可以声明 N 个拦截器可以实现登录的判断以及用户权限的判断等,这些就交给开发者自由发挥了。谈到这部分还会存在一个问题就是如何才能在一个地方判断出所有的页面哪些需要登录,哪些不需要登录,如果这时候保存两个非常大的列表,一个用于保存需要登录的页面,另一个保存不需要登录的页面,将会是非常不合适的了。
168171

169172
![](images/1debbf1bd2ec2ae6dae815026b5be2b7e2622bdc.png)
170173

171-
**标识目标页面信息 : 配置 extra 参数**
174+
## **标识目标页面信息 : 配置 extra 参数**
172175

173176
所以接下来分享一下如何配置页面的参数,刚刚提到了 “All In One”,这是什么意思呢?其实就是希望所有页面中的配置都能够浓缩到这一个页面中,也就是高内聚低耦合的思想,不希望页面的配置逃出页面,配置到像 Manifest 的其他地方。像在拦截器中配置哪些地方需要登录哪些不需要登录的话就违背了刚才提出的这个原则,ARouter 框架的设计思想就是希望所有的属性标注在自己的页面中。可以看一下页面中标注的页面注解,如下图所示可注解中在 IDE 的提示中有 extras 这样的参数,大家看到这个数字应该非常熟悉,这个数字就是 int 的最小值,而为什么 extras 这个参数是 int 呢?其实是因为 int 本身在 Java 中是由 4 个字节实现的,每个字节是 8 位,所以一共是 32 个标志位,去除掉符号位还剩下 31 个,也就是说转化成为二进制之后,一个 int 中可以配置 31 个 1 或者 0,而每一个 0 或者 1 都可以表示一项配置,这时候只需要从这 31 个位置中随便挑选出一个表示是否需要登录就可以了,只要将标志位置为 1,就可以在刚才声明的拦截器中获取到这个标志位,通过位运算的方式判断目标页面是否需要登录,这样是简单并且高效的,因为位运算的速度要远远高于字符串比对以及其他的方式的,而且一个 int 值就可以提供 31 个开关。目前而言没有一个目标页面需要配置 30 多个属性,所以使用 int 是足够的,而开发者只需要实现一个简单的位运算的工具类就可以提取出二进制 int 中的每一位,并对其中每一个值进行判断。
174177

@@ -178,7 +181,7 @@ ARouter 大致有以下 7 个优势:
178181

179182
![](images/607f8c4e6555b2da3777d40d95e66ad01b9fa931.png)
180183

181-
**模块间通信解耦 :控制反转**
184+
## **模块间通信解耦 :控制反转**
182185

183186
除此之外,另一个比较重要的问题就是如何实现模块间的通信解耦。实现组件化的时候希望对于不同的组件进行分别打包,而且模块之间应该不存在任何依赖,可以看出下图中左边的图中的四个组件完全是耦合依赖的,这样就导致四个组件之间根本无法解耦,所以打包的时候也必须一起打包,否则就会出现 No Class Found 的问题,所以现在的实现是如下图右边所示的,通过 IoC 容器,也就是控制反转容器将耦合解开。为什么这样能将耦合解开呢?其实是因为这样就可以让各个组件之间不产生直接依赖,而是通过 IoC 控制反转容器拿到对方的实例,这样我们在写代码的时候就不会存在直接依赖的问题。而 ARouter 本身也是一个 IoC 容器,它在实现这部分功能的时候用到的一个元素就是 Service,如果大家做过服务端开发的话就会对于 Service 很熟悉了,Service 就是将一部分功能和组件封装起来成为接口,以接口的形式对外提供能力,所以在这部分就可以将每个功能作为一个 Service,而 Service 的实现就是具体的业务功能,这部分也需要通过 IoC 容器进行获取。这样整个的流程将通过用户的直接依赖转化成通过控制反转容器依赖的这种形式。
184187

@@ -198,20 +201,21 @@ ARouter 大致有以下 7 个优势:
198201

199202
![](images/80de41e61c06241dfec82e64e1adf9385d2e5971.png)
200203

201-
**解决运行期动态修改路由的问题**
204+
## **解决运行期动态修改路由的问题**
202205

203206
然后需要分享的就是如何解决运行期动态修改路由的问题。如下图所示,这种情况下只需要实现一个服务就可以了,从下图也可以看出为什么说 ARouter 的组件都是自举的,因为服务的查找还是需要依赖于底层路由的查找的,所以服务功能的实现是由路由层作为基础的,并且服务是用来解决动态修改路由的问题的,所以只需要实现一个服务。其实这个 PathReplaceService 就是 ARouter 提供的一个服务,是在 ARouter 的 Frossard 层提供的服务,其实就是一个接口,只需要将其实现并标注上就可以,因为有自动注册的机制,所以在 APP 启动的时候就会注册到 ARouter 框架上,这样之后框架在跳转的时候就会跳转到这个服务,而如果没有实现,框架就无法调用,自然也就不会有这部分功能。这样就实现了 ARouter 框架的非常好的可扩展性,后期 ARouter 框架不需要更改其底层基础,只需要声明更多的服务,由用户主动实现,并在最后运行期的时候通过自动注册的方式将这些服务加载到框架中。
204207

205208
![](images/0541ad5176d509e1c6b5505e16665d67e87a3dd3.png)
206209

207210
而对于 PathReplaceService 这个服务而言,可以看到它有三个方法,首先 init() 用于初始化,下面的两个方法分别是 forString() 以及 forUri()。forUri() 是从外部通过 URI 的形式跳转到页面的时候会使用到的一个方法,参数中的 URI 就是原始的 URI,如果你有需要的话可以在这个方法中按照自己的一些逻辑和规则进行替换,然后直接 return 回来就可以了。这里 return 之后就会交给 ARouter 的框架继续处理,这时候就实现了对于目标页面的重定向。而 forString() 则是在正常情况下通过 ARouter 的 API 写代码的时候会使用到的方法。以上的这两个方法可以使用同样的逻辑来做,实现运行期动态地修改路由。
208211

209-
**解决降级问题**
212+
## **解决降级问题**
213+
210214
接下来分享的就是关于解决降级的问题。其实这部分的方法和刚才的方法是异曲同工的,只需要实现另一个服务就好了,ARouter 在发展中会越来越多地为大家提供各种服务让用户自己进行具体的实现,当然如果不实现也不会有这部分功能,如果 APP 实现了降级服务,那么随便标识一个注解就可以了,当然这个注解是由用户决定的,可以选择自己喜欢的规则,可以将这些服务都放在不同的分组下或者都放在同一个分组下。而现在相当于放在了 SDK 这个分组下面,对于这一部分只需要实现 onLost() 方法就可以了,ARouter 如果发现在目标跳转的情况下失败了,就会回调这个 onLost() 方法。onLost() 方法的第二个参数 postCard 翻译过来就是明信片,这里面就包含了本次跳转中所有的内容,通过拿到这些内容就可以实现自己的降级方案。下图中所列举的例子是通过跳转到第三方的 H5 的错误页面来解决的,因为 APP 不能够重复发布,但是 H5 是可以重复发布的,所以可以通过 H5 的方式解决降级问题,把去向的目标页面作为目标的参数传递到 H5 中。
211215

212216
![](images/5ee8813886bef58020d04c76b26db1f57e8c1e43.png)
213217

214-
**四、未来的开发计划**
218+
# **四、未来的开发计划**
215219

216220
最后想分享的就是 ARouter 的未来开发计划。未来 ARouter 会支持插件化并且支持生成映射关系文档,因为插件化是现在很多大型 APP 中会使用的技术方案,很多的 Dex 和功能是动态地下发到 APP 中的,而在这种情况下,是无法找到所有的 Dex 文件的,也就是对于没有加载过的 Dex 而言,里面的映射关系是跳转不过去的,所以一旦 Dex 文件位置发生变动,常规的方案是无法找到 Dex 的,也不能实现映射文件初始化,这一部分会在后面的版本中进行支持。因为像手淘和 360 等很多插件化的方案之后也许会开源,这样可能越来越多的 APP 会支持插件化,如果 ARouter 作为一个技术组件如果不能支持插件化的话,就会造成麻烦。
217221

Binary file not shown.

0 commit comments

Comments
 (0)