1.前言

$ e7 |  ~% L) i7 @7 B& t3 T5 h* P/ e2 s

当前,Android路由框架已经有很多了,如雨后春笋般出现,大概是因为去年提出了Android组件化的概念。当一个产品的业务规模上升到一定程度,或者是跨团队开发时,团队/模块间的合作问题就会暴露出来。如何保持团队间业务的往来?如何互不影响或干涉对方的开发进度?如何调用业务方的功能?组件化给上述问题提供了一个答案。组件化所要解决的核心问题是解耦,路由正是为了解决模块间的解耦而出现的。本文阐述了考拉Android端的路由设计方案,尽管与市面上的方案大同小异,但更多的倾向于与考拉业务进行一定程度的结合。

, o6 H, e5 F* L: J. w/ W1.1 传统的页面跳转

' j* K' U4 f8 a- F+ Q9 g0 O页面跳转主要分为三种,App页面间跳转、H5跳转回App页面以及App跳转至H5。

/ g3 z4 e% Z# r: I6 VApp页面间跳转

) x# t8 E& s9 D9 d9 AApp页面间的跳转,对于新手来说一般会在跳转的页面使用如下代码:

1 V* {7 M. p2 {  _8 h; a, l4 q  hIntent intent = new Intent(this, MainActivity.class);intent.putExtra("dataKey", "dataValue");startActivity(intent);

对于有一定经验的程序员,会在跳转的类生成自己的跳转方法:: w0 e+ \. J/ ^6 ]' m5 y* [4 w

public class OrderManagerActivity extends BaseActivity {    public static void launch(Context context, int startTab) {        Intent i = new Intent(context, OrderManagerActivity.class);        i.putExtra(INTENT_IN_INT_START_TAB, startTab);        context.startActivity(i);    }}

无论使用哪种方式,本质都是生成一个Intent,然后再通过Context.startActivity(Intent)/Activity.startActivityForResult(Intent, int)实现页面跳转。这种方式的不足之处是当包含多个模块,但模块间没有相互依赖时,这时候的跳转会变得相当困难。如果已知其他模块的类名以及对应的路径,可以通过Intent.setComponent(Component)方法启动其他模块的页面,但往往模块的类名是有可能变化的,一旦业务方把模块换个名字,这种隐藏的Bug对于开发的内心来说是崩溃的。另一方面,这种重复的模板代码,每次至少写两行才能实现页面跳转,代码存在冗余。5 P* E2 R2 s" F8 \

H5-App页面跳转; I" j: C" y3 @1 @

对于考拉这种电商应用,活动页面具有时效性和即时性,这两种特性在任何时候都需要得到保障。运营随时有可能更改活动页面,也有可能要求点击某个链接就能跳转到一个App页面。传统的做法是对WebViewClient.shouldOverrideUrlLoading(WebView, String)进行拦截,判断url是否有对应的App页面可以跳转,然后取出url中的params封装成一个Intent传递并启动App页面。0 k' w$ L, i) y; ]$ d  |7 \

感受一下在考拉App工程中曾经出现过的下面这段代码:

: U! z4 o( V& j8 w9 u/ U; h% vpublic static Intent startActivityByUrl(Context context, String url, boolean fromWeb, boolean outer) {    if (StringUtils.isNotBlank(url) && url.startsWith(StringConstants.REDIRECT_URL)) {          try {            String realUrl = Uri.parse(url).getQueryParameter("target");            if (StringUtils.isNotBlank(realUrl)) {                url = URLDecoder.decode(realUrl, "UTF-8");            }        } catch (Exception e) {            e.printStackTrace();        }    }    Intent intent = null;    try {        Uri uri = Uri.parse(url);        String host = uri.getHost();        List pathSegments = uri.getPathSegments();        String path = uri.getPath();        int segmentsLength = (pathSegments == null ? 0 : pathSegments.size());        if (!host.contains(StringConstants.KAO_LA)) {            return null;        }        if((StringUtils.isBlank(path))){            do something...            return intent;        }        if (segmentsLength == 2 && path.startsWith(StringConstants.JUMP_TO_GOODS_DETAIL)) {            do something...        } else if (path.startsWith(StringConstants.JUMP_TO_SPRING_ACTIVITY_TAB)) {              do something...        } else if (path.startsWith(StringConstants.JUMP_TO_SPRING_ACTIVITY_DETAIL) && segmentsLength == 3) {             do something...        } else if (path.startsWith(StringConstants.START_CART) && segmentsLength == 1) {             do something...        } else if (path.startsWith(StringConstants.JUMP_TO_COUPON_DETAIL)                || (path.startsWith(StringConstants.JUMP_TO_COUPON) && segmentsLength == 2)) {            do something...        } else if (canOpenMainPage(host, uri.getPath())) {             do something...        } else if (path.startsWith(StringConstants.START_ORDER)) {             if (!UserInfo.isLogin(context)) {                do something...            } else {                do something...            }        } else if (path.startsWith(StringConstants.START_SAVE)) {             do something...        } else if (path.startsWith(StringConstants.JUMP_TO_NEW_DISCOVERY)) {              do something...        } else if (path.startsWith(StringConstants.JUMP_TO_NEW_DISCOVERY_2) && segmentsLength == 3) {             do something...        } else if (path.startsWith(StringConstants.START_BRAND_INTRODUCE)                || path.startsWith(StringConstants.START_BRAND_INTRODUCE2)) {              do something...        } else if (path.startsWith(StringConstants.START_BRAND_DETAIL) && segmentsLength == 2) {              do something...        } else if (path.startsWith(StringConstants.JUMP_TO_ORDER_DETAIL)) {              if (!UserInfo.isLogin(context) && outer) {                do something...            } else {                do something...            }        } else if (path.startsWith("/cps/user/certify.html")) {               do something...        } else if (path.startsWith(StringConstants.IDENTIFY)) {             do something...        } else if (path.startsWith("/album/share.html")) {              do something...        } else if (path.startsWith("/album/tag/share.html")) {              do something...        } else if (path.startsWith("/live/roomDetail.html")) {               do something...        } else if (path.startsWith(StringConstants.JUMP_TO_ORDER_COMMENT)) {             if (!UserInfo.isLogin(context) && outer) {                do something...            } else {                do something...            }        } else if (openOrderDetail(url, path)) {            if (!UserInfo.isLogin(context) && outer) {                do something...            } else {                do something...            }        } else if (path.startsWith(StringConstants.JUMP_TO_SINGLE_COMMENT)) {              do something...        } else if (path.startsWith("/member/activity/vip_help.html")) {            do something...        } else if (path.startsWith("/goods/search.html")) {            do something...        } else if(path.startsWith("/afterSale/progress.html")){              do something...        } else if(path.startsWith("/afterSale/apply.html")){              do something...        } else if(path.startsWith("/order/track.html")) {             do something...        }    } catch (Exception e) {        e.printStackTrace();    }    if (intent != null && !(context instanceof Activity)) {        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    }    return intent;}

这段代码整整260行,看到代码时我的内心是崩溃的。这种做法的弊端在于:" I3 h9 k# j1 H$ R+ d

3 g1 J' |! g, s* ~9 Q  X

判断不合理。上述代码仅判断了HOST是否包含StringConstants.KAO_LA,然后根据PATH区分跳转到哪个页面,PATH也只判断了起始部分,当URL越来越多的时候很有可能造成误判。

% J. [/ @! Y9 I

耦合性太强。已知拦截的所有页面的引用都必须能够拿到,否则无法跳转;+ W# ^& [8 P/ O. r

代码混乱。PATH非常多,从众多的PATH中匹配多个已知的App页面,想必要判断匹配规则就要写很多函数解决;% o1 K# h! S* C# ^8 z; a0 S( ~

拦截过程不透明。开发者很难在URL拦截的过程中加入自己的业务逻辑,如打点、启动Activity前添加特定的Flag等;# B2 l. Z- C& ~3 Q% u, I# V8 t

没有优先级概念,也无法降级处理。同一个URL,只要第一个匹配到App页面,就只能打开这个页面,无法通过调整优先级跳转到别的页面或者使用H5打开。

I; f% h, n0 IApp页面-H5跳转

% v4 i; Q2 B3 t8 h) |5 h& z这种情况不必多说,启动一个WebViewActivity即可。) v- f* p: j0 W% A

1.2 页面路由的意义& s  c3 z# {. n0 o* D6 l1 ~* r" Z$ S

路由最先被应用于网络中,路由的定义是通过互联的网络把信息从源地址传输到目的地址的活动。页面跳转也是相当于从源页面跳转到目标页面的过程,每个页面可以定义为一个统一资源标识符(URI),在网络当中能够被别人访问,也可以访问已经被定义了的页面。路由常见的使用场景有以下几种:

' ~. ^/ b: G, M8 F# S8 o8 @: i3 Q2 l% [( k- I

App接收到一个通知,点击通知打开App的某个页面(OuterStartActivity)

3 V, y5 e- U0 P8 f+ ?

浏览器App中点击某个链接打开App的某个页面(OuterStartActivity)

/ J# _* O. [- ~2 P' b( R+ K

App的H5活动页面打开一个链接,可能是H5跳转,也可能是跳转到某一个native页面(WebViewActivity)

* {% v* ~3 q! q& g8 T# |2 m

打开页面需要某些条件,先验证完条件,再去打开那个页面(需要登录)

c# b- z  {) @4 ^* x. I

App内的跳转,可以减少手动构建Intent的成本,同时可以统一携带部分参数到下一个页面(打点)' S) c9 m2 D3 l! M& F9 i* S2 A. v

除此之外,使用路由可以避免上述弊端,能够降低开发者页面跳转的成本。: z2 E3 e4 z. X$ l( c2.考拉路由总线

& X* J3 S( C8 D9 u, @9 a" Y- L) I

! d8 i2 t. ~, V( G9 Y- J2.1 路由框架

8 Z; l; n, I! v$ [" W/ k, P- l

( n6 |1 J; i6 C8 A  [* b8 X

考拉路由框架主要分为三个模块:路由收集、路由初始化以及页面路由。路由收集阶段,定义了基于Activity类的注解,通过Android Processing Tool(以下简称“APT”)收集路由信息并生成路由表类;路由初始化阶段,根据生成的路由表信息注入路由字典;页面路由阶段,则通过路由字典查找路由信息,并根据查找结果定制不同的路由策略略。. E1 B9 g# H" {6 n& e

9 {+ C* }' k& x

2.2 路由设计思路

java路由总线_网易考拉Android客户端路由总线设计相关推荐

  1. 网易考拉Android客户端路由总线设计

    1.前言 当前,Android路由框架已经有很多了,如雨后春笋般出现,大概是因为去年提出了Android组件化的概念.当一个产品的业务规模上升到一定程度,或者是跨团队开发时,团队/模块间的合作问题就会 ...

  2. 网易考拉Android客户端路由总线设计 1

    1.前言 当前,Android路由框架已经有很多了,如雨后春笋般出现,大概是因为去年提出了Android组件化的概念.当一个产品的业务规模上升到一定程度,或者是跨团队开发时,团队/模块间的合作问题就会 ...

  3. 网易考拉Android客户端网络模块设计

    本文来自网易云社区 作者:王鲁才 客户端开发中不可避免的需要接触到访问网络的需求,如何把访问网络模块设计的更具有扩展性是每一个移动开发者不得不面对的事情.现在有很多主流的网络请求处理框架,如Squar ...

  4. 非静默授权没有弹出弹框_网易考拉Android统一弹框

    作者:钱成杰 链接:https://blog.csdn.net/jessicaiu/article/details/82739334 背景 在快速开发的背景下,经历了n个版本后的考拉Android A ...

  5. 如何实现接口统一入口_网易考拉Android App如何实现统一弹框

    摘要 在快速开发的背景下,经历了n个版本后的考拉Android App中已经存在了各种各样看似相同却各有差别的弹框样式.其中包括系统弹框和自定义弹框,并且在线上时常会出现IllegalArgument ...

  6. android app.build文件_网易友品 Android 客户端组件化演进

    原文作者:简书 - 四单老师 项目背景 主站业务经历了长期的迭代维护,业务的增长同时带来每个版本业务量繁重,迭代周期很快.同时团队也在不断的扩张,对应拆分了组内不同的业务线对接不同业务线的需求,最初的 ...

  7. 网易考拉规则引擎平台架构设计与实践

    此文已由作者肖凡授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 背景 考拉安全部技术这块目前主要负责两块业务:一个是内审,主要是通过敏感日志管理平台搜集考拉所有后台系统的操作 ...

  8. 规则引擎 drools_网易考拉规则引擎平台架构设计与实践

    背景 考拉安全部技术这块目前主要负责两块业务:一个是内审,主要是通过敏感日志管理平台搜集考拉所有后台系统的操作日志,数据导入到es后,结合storm进行实时计算,主要有行为查询.数据监控.事件追溯.风 ...

  9. 数据采集时总提示未登录_做电商必须学会这一招!教你用爬虫工具免费采集网易考拉商品数据...

    本文主要介绍如何使用后羿采集器的智能模式,免费采集网易考拉商品的价格.累计评价.商品图片等信息. 采集工具简介: 后羿采集器是一款基于人工智能技术的网络爬虫软件,只需要输入网址就能够自动识别网页数据, ...

最新文章

  1. 初探 Unix 操作系统
  2. PAT甲级1134 Vertex Cover :[C++题解]顶点覆盖、图论、用结构体存边,bool数组判断
  3. Codeforces 982 C. Cut 'em all! 图的遍历
  4. JavaScript的代码编写注意事项,建议收藏!
  5. 滴水穿石-07Java开发中常见的错误
  6. Oracle 12.1.0.1 GI+DATABASE打PSU
  7. webpack多页面开发与懒加载hash解决方案
  8. 网页打开摄像头_只要5分钟,快速掌握摄像头课件直播技巧
  9. idea 中文字体 自动变_提高工作效率,我推荐讯飞语记,瞬间语音秒变文字
  10. 分享一下在用的私人小主机
  11. 【最强实习生】20场Android面试斩获大厂offer,来看看我都会些什么
  12. 猫哥教你写爬虫 031--爬虫基础-html
  13. 条形码和区块链将彻底改变零售业
  14. html书写表单laber,day02_HTML表格列表表单
  15. 测试工程师的面试基础题目
  16. 正则表达式验证IP地址合法性
  17. 认识Axure线框图组件
  18. 计算机网络相关拓展材料
  19. Sketch哪个版本好用啊?
  20. Activate、Deactivate 事件 Activate ThrottleEvent;

热门文章

  1. 聚观早报 | 恒大汽车或将被并购;比亚迪今年将进入丹麦市场
  2. php中计算出生天数,php天数计算及生日算出年龄的方法
  3. ping回显无法访问目的主机问题解决过程
  4. 韩国仁川机场乐天免税店买买买!
  5. php的核心架构如下图,thinksns 核心架构及目录结构
  6. RTB,PDB,PD,程序化购买的集中方式。
  7. selinux m4语言语法
  8. 使用 ipmitool 实现 Linux 系统下对服务器的BMC管理
  9. NJ 时钟自动调整功能(SNTP)
  10. 第三代酷睿i3处理器_高性价比装机方案 十代酷睿i310100F配GTX1650S组装机配置清单...