Rabbits 1.0.0

cover

如果只是想查看Rabbits 1.0.0的变化以及新版本的使用方法,可以直接查看最后一节。

路由在移动开发中的作用

除了路由器,最早知道路由这个概念还是很早之前接触到的PHP框架,通过对url进行匹配、解析,将请求转发到某个方法并渲染前端模板。事实上路由就是把一个请求转发到对应的处理器上。

和WEB开发中URL对应页面或后端处理逻辑类似,移动端“打开页面”的动作,同样可以当做请求处理。Activity之间的跳转使用Intent来完成,Intent其实就可以看作是向ActivityManager发送的一个请求。

Intent intent = new Intent(this, NextActivity.class);
this.startActivity(intent);

以上代码在项目中可能非常常见,那么为什么还要剥离出一个模块去代替已经很简洁的Intent模式呢?

最开始的需求开始来自于项目。对我来说,就是项目中有很多WEB页面和NATIVE页面相互跳转的逻辑,最初的实现就是覆写shouldOverrideUrlLoading方法,得到URL后根据domain、path等来进行匹配,再分别使用Intent打开对应的Activity。这样当然也能满足需求,但是当页面越来越多的时候,处理跳转逻辑的类就包含了大量的if-else语句,以及重复的逻辑,修改起来也比较麻烦。因为项目没有使用热更新,如果增加一个到某个页面的跳转链接,也需要更新客户端,同时让跳转逻辑继续膨胀。

开发Rabbits最初的动机就来源于此,使用URL作为请求的标识符,让Rabbits作为NATIVE和WEB页面跳转的桥梁

当然使用URL的好处就不仅于此了,在路由这件事上,URL相当于对页面的抽象,而Rabbits相当于对跳转实现(Intent)的抽象。抽象了一层之后就可以在抽象与实现之前加入更多的可能性。比如URL的解析,可以解析到页面,同样也可以解析到静态方法,或者其他各种服务;比如可以实现包含各种逻辑的拦截器;再比如实际使用Intent跳转的步骤,因为Intent这层已经被抽象了,只需要实现抽象接口,我们可以开发出其他具体实现,比如Fragment的跳转,比如仅仅是打印一行Log。

这么来看,Rabbits(或者说路由)不仅仅是Intent的替代品,而是“跳转”这件事的替代品,是一个抽象下的另一种更丰富的实现。

Rabbits 1.0.0 幕后

调用链

Rabbits最初版完成之后,我就一直在项目中使用了,也确实没出过问题,项目不大,性能也没有压力(在启动时会在主线程加载Assets目录下的json文件,不过也确实测试过即便再增加几百行,对耗时也只有几ms的影响)。但毕竟原来的结构是在没使用路由之前设计的,在实际项目中用到之后还是能发现设计上考虑不周的地方。比如mappings.json实际价值不高,拦截器的逻辑冗余,以及obtain方法对API调用流的破坏等等。

正是使用中的这些不适,让我重新思考Rabbits的结构。当我重新思考Rabbits的实现方式时,我发现Rabbits内部的种种逻辑与调用关系,其实就是一个以startActivity()为结尾的调用链,最初的URL经过一层层的加工,最终成为构建Intent的参数。当我得到这个结论之后,我就有了对Rabbits重构的思路:

把URL到startActivity()这整个过程,看做一个调用链,每一个步骤都是调用链上的一个环节,最初的跳转请求(在1.0.0中由Action类实现)在这个链上传递,最终由最后一个环节执行startActivity()完成跳转。

三个主要环节

有了链条,就要开始添加环节了,依据早期版本的结构,我提取出三个主要环节:匹配组装执行

3steps

匹配
通过URL匹配到在编译期间生成的TargetInfo。涉及到URL的分级匹配、参数解析。

组装
根据TargetInfo、请求参数、控制参数等创建Intent对象(Fragment对象)。

执行
获取前一个环节生成的Intent对象(Fragment)根据传入的跳转起始对象,执行startActivity()或类似方法,完成跳转过程。

拦截器

通过前文的解释,你可能已经猜到了,事实上每一个环节,都是一个拦截器。因为有调用链的结构作为基础,每一个环节,都可以当做是这个调用链上的一个拦截器,而自定义的拦截器,当然也可以和上面三个主要环节一样,“平等的”嵌入到调用链条当中。下图展示了1.0.0版本Rabbits的工作流程。

workflow

Rabbits 1.0.0

改变

核心结构敲定之后,注解、API等就像顺水推舟。完成后的1.0.0版本从内部完成了进化。如果你之前有了解过Rabbits,有3个非常明显的变化。

  1. 取消了mappings.json的设计,显然启动时的耗时更少,同时结构更简单,代码量降低。
  2. 初始化流程更加简便,scheme、domain配置,拦截器,Fallback处理等等都在一个链式调用里完成。(这个变化也带来一个副作用,如果拦截器比较多,那么这个链式调用会很长,可以通过减少匿名内部类的数量解决。当然,不使用链式调用也是可以的。: P)
  3. 多Module的使用变得简单,通过编译参数解决Module间的依赖和关联问题(主要是路由表的整合),同时也不依赖运行时。

延续

1. From-To-Start 模式

我把这个链式调用叫做 From-To-Start 模式,简称FTS(cool)。FTS是Rabbits最初版本就使用的,当时的想法根据对“跳转”这个过程的直觉理解设计API,跳转其实就是从一个页面到另一个页面的过程,那么api干脆就叫做from和to好了。于是就有了如下调用模式:

Rabbit.from(this).to(P.TEST).start();

经过在项目中的实践,我发现FTS符合直觉的调用方式真的很上瘾,以至于有些地方再用到Intent的时候反而觉得很别扭。

2. P文件

在设计Rabbits初版的时候,我就意识到如果项目中直接使用字符串常量作为跳转的URL,那么项目中会存在难以维护的隐患:URL改动可能涉及到很多地方,不同URL甚至可能对应同一个页面,如果要改变URL的指向涉及到的地方就更多了。我当然想到了使用常量来减少字面量的数量,但是既然可以通过注解完成路由表的注册过程,那么常量为什么不能通过注解处理器生成呢?

mappings.json存在的时候,通过page name就可以直接生成对应的字符串常量定义类P.java。但现在这个便捷条件已经消失了。1.0.0版本的P文件,根据URL直接转换而成,为了兼容空字符串(或根路径),根据URL生成的常量名都会带有P_前缀。但是使用起来仍然很方便。

使用

这里介绍下Rabbits的常规使用,更多内容可以参见Github上的wiki

1. Cradle引入

dependencies {
    implementation "com.kyleduo.rabbits:rabbits:1.0.0"
    annotationProcessor "com.kyleduo.rabbits:compiler:1.0.0"
}

2. 注解

@Page("/test")
public class TestActivity extends AppCompatActivity {}

@Page(value="/test", alias="TEST", flags=1<<4, variety=["/test/{param}"])
public class TestActivity extends AppCompatActivity {}

最简单的注解可以只包含path部分,Rabbits会在scheme和domain合法的情况下对path进行匹配。针对这种注解,Rabbits生成的P文件会包含如下常量定义:String P_TEST = "/test";

第二个例子稍微复杂一点,alias会影响P文件中生成的常量名,其他参数的含义和使用方法参见wiki和demo项目吧。

3. 初始化

Rabbit.init(RabbitConfig.get()
                    .schemes("demo")
                    .domains("rabbits.kyleduo.com"));

这应该算是最简单的初始化了,设置好scheme和domain即可。scheme和domain都可以设置多个,第一个会作为默认值。

4. From-To-Start 模式

Rabbit.from(this).to(P.TEST).start();

ALL DONE 以上就是从引入依赖到完成第一次跳转的全部步骤啦。

References

项目地址:Github
项目wiki:wiki
原文链接:kyleduo.com

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,012评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • 习惯一个人 最近刷微博看到一组图片很窝心,男孩问奶奶他结婚了会送什么礼物,奶奶打趣到长成这样还会有姑娘看上,男孩...
    Locked陌生人阅读 144评论 0 0
  • 有人在外狠狠的拍着我家的门,开门一看,站在面前的人,非常生气的表情。 他一开口就说:“能不能小点声音啊? 我都在这...
    朵加阅读 204评论 0 0
  • 怨妇一词,在《新华词典》中的解释是:指丧夫或与丈夫别离很久的女人。延伸的意思是被男人抛弃了,心存不满的女人。 封建...
    奔跑的蜗牛妈妈阅读 1,155评论 2 1