2014 年 6 月份,in 发布了第一个版本。到目前为止,已经经历了几十个版本的迭代。在 1.0 时代,APP 的特色是小、快、灵。当时产品逻辑并不复杂,投入的资源不是特别多。由于处于探索期,因此产品的迭代很是快,为了与之适应,in 采用了简单的单工程的形式组织整个产品结构,高结构的层次也只有几层,很是浅,如图 1 所示。服务器
▲图 1网络
为了兼容 H5 跳转,in 参考了 H5 的一种路由协议做为跳转的支持。该时期,in 的用户量增加迅速,1.0 末期已经达到了近 2500 万。在这个框架下,in 一直衍生到 1.9 版本,这段时期使用轻量结构比较适合小步快走,即迭代很是快的形式,但这种形式存在一个明显的缺点,即扩展性较差,个别类臃肿,不适合协同开发。随着业务逻辑愈发复杂,参与人员不断增长,如何提升协同开发效率成为当务之急。架构
in 在 2.0 时代有三个亟需解决的问题:框架
产品逻辑日趋复杂。复杂表如今两方面:一是演进很是快;二是反复。这是最致命的,由于开发过程当中 PD 可能会临时作一些需求上的变动。所以,此时的框架必须适应产品的复杂化。 测试
代码复用性差。1.0 时代,新模块的开发主要基于原有的模块,仅仅在原有的模块上作一个很小的改动,代码实现上却须要进行大幅度的调整。spa
业务逻辑与基础功能杂糅在一块儿。由于业务上的一些变动经常会触及底层的东西,致使稳定性不足。代理
▲图 2日志
基于这三点,in 在 2.0 时代进行了如图 2 所示的重构。blog
首先,将全部业务分红独立的模块,同时考虑产品逻辑中可能没有想到的模块。以 in 为例,in 有图片详情页,但从产品逻辑看,产品分为我的中心、话题、品牌站等模块,这些模块都有各自的图片详情页,图片详情页并非一个独立的业务线。初期,in 严格按照产品线去划分业务逻辑模块,因此开发出好几套图片详情页,这就是代码复用性差。经验教训是,模块不该彻底由 PD 决定,咱们必须很是熟悉产品结构,清楚有没有共性的模块能够单独抽离出来。接口
其次,业务模块必须具备必定的配置性,便可扩展性。沿用刚才的例子,咱们但愿我的中心的图片详情页具备显示用户打上去的标签、贴纸等的功能,但品牌详情页可能并不须要这类功能,因此业务模块必须达到可配置。分好业务模块后,各个模块之间相互独立,但必然存在公共的功能,所以须要有一个底层的公共类库作支持。公共类库主要包括了一些基础功能,好比网络请求、图片解析、本地日志系统等。
最后,in 引入了许多第三方功能,而公共模块是一个独立工程。in 容许公共模块直接使用第三方库,但不容许其它模块单独使用。所以,第三方库达到统一管理。同时,in 还沿用并强化了 1.0 时代的路由协议,推送能够经过这套协议跳转到推送页面。
到此,in 基本解决了以前谈到的三个问题,更重要的是提升了 QA 测试效率。
以往须要等全部功能开发完成才能交付给 QA,分模块后,每个业务模块均可以不依赖其余模块独立运行。当一个模块本身的业务开发完成后,均可直接交付给 QA。但与此同时,产品又产生了新的问题,即公共类库臃肿,难维护,迁移成本高。
2.0 时代,in 的用户量从 1.0 时代的近 2500 万增加到 7000 万。in 意识到每一次小的更新都会影响用户的体验,所以告别了原先快速迭代的发展模式,转为求稳。从开发的角度来讲,是要提供更优质的服务。
▲图 3
针对 2.0 时代产品公共类库臃肿的问题,in 在框架上作了如图 3 所示的改进。首先,上层沿用 2.0 时代的形式,但对公共模块进行拆分,将公共业务抽成代理层,而且引入服务化的概念,将每个机组功能都抽成独立的服务,好比网络请求、图片上传,本地日志等。这一版改进后,服务都被独立抽出,相互之间是隔离的,每一个服务均可以交由不一样的人去维护,内部高类聚。
与此同时,每一个服务都须要有容错性,每一个模块都须要有兜底方案,保证本身的输出是稳定的,本身内部的问题不会影响其余服务。
另外,底层服务不多被上层的业务代码入侵,可尽可能经过协议或者是 API 的形式支持上层的业务逻辑,作到最轻量化级的接入。in 还对第三方库作了封装,将其经过代理的模式与本身的业务代码隔离,这样就能够灵活地替换第三方类库,而且大大下降维护成本。
服务化过程当中,in沿用了以前的通讯模块,而且加入了一个统计框架。这个框架着重突出了服务化的概念,而且是本地服务化。它的优点在于很是的独立,且具备很高的扩展性,每加入一个新的服务,都不会影响到其余的服务,而且在整个架构的层面上来说,每个服务之间相互依赖的关系、调用的顺序均可以很快地整理出来。同时,它还给in的产品矩阵打下了一个很好的基础,将来若是推出一些新的APP,须要引用in老的代码时,只须要选择须要接入的那些服务,就能很快理出新的APP的架构图,而且配置起来。
▲图 4
图 4 为 in 内统计框架,最大的特色是自动化、无侵入式。业界不少统计框架在路径统计层面,主要是统计 Activity 层以及 Fragment 层,可是 in 的不少页面是经过 View 等其余形式实现的,所以没法经过现有的一些统计框架进行页面统计。对此,in 把全部的页面都抽成 layer 的抽象概念,把全部的 layer 经过用户的行为路径压到一个 layer 栈内,最终以一个列表的形式发到服务器,而后在服务器创建一个数据仓库,再经过 BI 部门整理数据仓库得出每一个用户的实际浏览路径,包括每一个页面的留存等。
▲图 5
耦合存在每一个模块内。业界很常见的是用 MVC、MVP 等模式进行必定的解耦,in 主要用 MVP 模式。为何 in 以前 Activity 经常写得特别臃肿?由于它不只作了 Model 层的事,并且作了表现以及控制上的事。解决办法是把 view 层单独抽离,由 Fragment 去作 View 层的展示,而 Activity 层只专一于对数据的处理,实现 View 层跟 Model 层之间不直接交互,而是经过一个接口的形式进行沟通。
灰度发布机制主要是为了支持产品的 A/B Test。in 的产品愈来愈复杂,用户量愈来愈多,为了实验性的功能不影响全部的用户体验,只能容许一部分特定用户看到新功能,而这须要经过代码层作控制,即灰度发布机制。如图 5 所示,灰度发布主要在业务层之上,它的配置所有由服务器端决定,确保每一个业务均可以作到灰度发布。
模块增长后,模块间通讯成为一个大问题,由于模块之间是不可见的。两个解决办法:第一是经过一套反射机制达到每一个模块间相对可见;第二是创建一套本身的基于观察者、订阅者模式的消息分发机制,in 的这套机制主要参考了 EventBus 以及谷歌最近开源的一个安卓响应式框架 Agera 相似于 RxAndroid ,这个框架能帮助模块间通讯的现成模型的构建。另外,模块间通讯有一个Sticky机制,问题就在于当页面未打开以前,数据已经先到了,那么该如何解决?就是经过 Sticky 机制,它能确保数据先到,页面再打开的时候,数据能顺利下发。
每一套框架都有本身的特色,可是万变不离其宗,最主要的是要适合当前的项目规模和体量,更好的与业务结合在一块儿,提升开发效率,下降维护成本。