首发于微信公众号: BaronTalk
安居客 Android App 距离上次的模块化/组件化重构已经两年多了,重构以后很好的支撑了两年多以来的业务发展。但这个世界老是在向前走的,没有任何一种架构可以一劳永逸的解决全部问题,外部环境的不断变化相应的也要求项目架构作出改变,以此来应对环境变化所带来的挑战。前端
本文分享的就是咱们安居客 App 团队此次向平台化转型的背景、转型过程当中所面临的问题、挑战、咱们的解决方案以及我我的在这个过程当中的收获和感悟。java
自 18 年以来的市场环境你们都知道,各大公司都在缩减开支、提高组织效率,但愿用更少的投入带来更多的产出,以此来应对经济下行带的不肯定性。在这个大背景下,集团在 18 年末作出了一系列人效提高、业务整合的动做。git
拿房产业务举例:58 App 里的房产业务以前是由北京的房产团队开发的,而整个安居客 App 是由我所在的上海安居客团队开发的。在此次调整以后,北京的房产团队做为一条垂直业务线来负责 58 App 和安居客 App 中的租房业务的开发,安居客团队则继续负责整个安居客 App(包含新房、二手房、内容、IM 等业务)的开发,同时还要开发 58 App 中二手房、新房、房产大内容等业务。github
这样一次调整给咱们带来了三大挑战:小程序
面对这些挑战咱们须要转变开发思惟、改进项目架构。后端
一次项目重构和架构升级,不仅是要解决当下的问题,更要为将来一到两年的业务发展提供支持。为了解决这一系列的问题,咱们联合 58 无线团队、58 房产团队、前端、后端多个团队的同窗一块儿发起了「木星计划」,也开启了咱们的平台化转型之路。微信
当公司的业务调整政策下来之后,团队面临的最急迫的一个选择是继续在 58 App 上维护老的房产代码,迭代新功能;仍是将安居客现有业务搬迁到 58 App 实现一套代码在两个 App 里运行呢?网络
为长远计,咱们最终选择了后者。但是要作到一套代码双端运行并不容易,58 App 和安居客 App 隶属于北京、上海两个不一样的团队开发,你们底层库不同,技术方案不同,开发模式也不同,要达到咱们的目的必然要费一番功夫。接下来我从总体到局部逐步介绍咱们在平台化演进过程当中的设计思路、遇到的问题和解决方案架构
所谓平台化,就是安居客 App 要做为一个平台来对平台上承载的各类垂直业务提供服务,每一个服务都须要对上层提供标准化的接口来支撑平台上各种垂直业务的功能。app
这一次的项目重构除了要转型平台型 App 支持其余业务的接入和将来业务的发展,还有更重要一点是要作到一套代码在 58 和安居客双平台运行。
要作到这一点,在现有的业务体系和代码体量下虽然工做量巨大,但大的思路确是很清晰简单的:
针对安居客 App ,咱们须要调整架构,引入一个平台中间层并针对平台中间层接口作安居客 App 侧的实现,并将垂直业务中本来调用平台接口的地方改成调用中间层接口。同时将以前的业务组件层细分为「平台级组件」和「业务级组件」,全部垂直业务再也不依赖平台级组件,只依赖平台中间层和业务级组件,并明确业务代码迁移的边界。
同时 58 无线团队的同窗也改进了他们的架构,和咱们同样引入了平台中间层及中间层在 58 App 上的实现,保证安居客业务迁移进来后能正常编译运行。
上面这两张架构图呈现了两个 App 架构调整的大方向,具体作了哪些、遇到了哪些问题我在下面的小节里一一介绍。
计算机领域的任何问题均可以添加一个中间层来解决。58 App 和安居客 App 做为两个不一样的平台,对业务层提供的能力是不同的,接口、方法名都是不同的。同一块业务代码要跑在两个不一样的平台,必然要引入一个中间层来抹平差别、屏蔽底层细节,同时对外提供统一的接口供业务层调用。
所以 58 无线团队、58 房产团队和咱们安居客团队三方协商,共同制定了一套平台中间层 API,而后两个平台再针对平台中间层 API 作具体实现。
为了便于后期管理、划清代码边界,咱们将平台中间层服务进一步细化,根据平台差别性将中间层划分为平台公共服务、安居客平台特有服务、58 平台特有服务等。以 Java Package 做为区分,划分到不一样的包结构下。一旦后期某一特有服务变成了公共服务,则将其往平台公共服务迁移。
在实现上,平台中间层会提供一系列 Service 接口供垂直业务调用,同时提供一个 PlatFormServiceRegistry 类用来注册和获取服务。
public class PlatFormServiceRegistry { ... /** * 注册服务 */ private void registService(Class serviceInterface, Class<? extends IService> serviceImpl) { if (serviceInterface != null && serviceImpl != null ) { classMap.put(serviceInterface.getName(), serviceImpl); } } /** * 获取服务 */ private <T> T getService(Class<? extends T> service) { ··· IService instance = serviceImplMap.get(service.getName()); if (null == instance) { try { Class<? extends IService> serviceClass = classMap.get(service.getName()); if (serviceClass != null) { instance = serviceClass.getConstructor().newInstance(); serviceImplMap.put(service.getName(), instance); } } catch (Exception e) { Log.d(TAG, e.toString()); } } return (T) instance; } }
在使用方式上,平台方首先要注册服务
//注册平台服务 PlatFormServiceRegistry.registeAppInfoService(AjkAppInfoServiceImpl.class);
业务方在使用服务的时候获取到对应的 Service 就能够调用相关方法了。
//使用平台服务 IAppInfoService appInfoService = PlatFormServiceRegistry.getAppInfoService(); appInfoService.getAppName(context);
安居客 App 因为历史缘由,Android 和 iOS 在与 H5 的交互协议上有不少不同(虽而后期统一过 JSBridge 协议,但不少历史遗留协议仍旧是不一致的),58 App 上的 Native 和 JS 交互协议更是和安居客不一致。为了解决这些问题咱们须要 Android 和 iOS、58 和 安居客有一个统一的 Native 与 JS 的交互协议;同时为了兼容历史协议,让业务平稳过渡,咱们还须要设计一套过渡方案。
在未实现协议统一的状况下,一个 H5 页面要上 58 App 和安居客 App 两个平台,须要支持两套协议,再加上安居客以前 Android、iOS 协议的不一致,一个 H5 页面最多可能须要支持 4~5 套协议。
为了实现协议的最终统一,而且让业务平稳过渡,咱们引入了一套过渡方案。在保留两个平台现有协议和 JSBridge SDK 的状况下,58 无线团队的同窗设计并开发了一个全新的 HybridSDK,过渡阶段三套协议并存,来不及调整的旧业务使用旧协议,新开发及本次要调整的业务使用新协议。
而后随着业务的迭代,不断废弃两个平台的自有协议,最终走向统一。
现有 App 内的页面跳转要么是 intent 跳转,要么是写死的路由跳转。在此次的平台化改造过程当中,咱们从 58 App 上也学到了不少东西,其中动态路由下发就是咱们学习并引入到安居客 App 中的。
简单的说就是 App 给各个页面定义好路由协议,App 在调用 API 的时候返回结果中会包含当前页面跳转到下一页面的路由协议,这就是所谓的动态路由下发。这样作会带来三个好处:
咱们此次的平台化改造,不管对安居客 App 仍是 58 App 来讲都是一次「大手术」,术后可否保证线上的稳定性、业务数据不受影响是很是重要的。就拿把安居客业务迁移到 58 App 这件事来讲,若是一股脑的用安居客迁移过去的房产业务代码替代 58 App 内的房产业务代码,就算咱们在技术上作到了绝对的稳定,也难保业务数据不会受影响。
好在得益于上面提到的动态路由下发方案,咱们能够作到在少许城市、少许用户上作灰度,让这部分人先试用安居客迁移过去的业务,其它用户继续使用老的房产业务,数据效果好再逐步加量,直到彻底替代。这样就能最大限度的下降影响,保证上线后的稳定性。
虽然在上一次的模块化/组件化改造过程当中咱们对各项垂直业务作了拆分、解耦,可是各业务仍是有不少重叠的业务,因而咱们将这些业务下沉到 CommonBusiness 组件中,同时这个 CommonBusiness 组件里还包含了一些 App 平台级别的基础功能,所以这个 CommonBusiness 组件成了个大而全的东西。
在此次的架构调整中,咱们为了实现业务的快速平移,将 CommonBusiness 和新房、二手房等几条垂直业务线的代码一股脑的迁移进了 58 App,这就直接致使了 58 App 体积的快速膨胀。因而在后期,咱们将 CommonBusiness 按能力拆分红了多个独立的组件,而且分为了「平台级组件」和「业务级组件」。平台级组件属于安居客平台特有,不随业务迁移;业务级组件属于多个垂直业务公用的组件,随业务代码一块儿迁移到 58 App。这一点在前面的架构图中有体现。
前面介绍中间层的时候提到,还有一部分底层库暂时没法统一,现阶段是经过引入中间层来解决的。但从长远来看,整个集团无线体系下,依赖的底层库仍是要走向统一。就拿分享组件来讲,现阶段安居客和 58 都是使用本身的 ShareSDK,而后中间层定义了一套分享接口,两个 App 分别调用本身的 ShareSDK 来实现接口知足业务的分享需求。为了进一步下降开发成本,避免重复造轮子,后期双方还须要统一使用同一个 ShareSDK,抛弃中间层。最终整个集团体系下全部的 App 的架构应该以下面这张图所示:
统一的平台层,不一样的宿主搭配上不一样的垂直业务,就是不一样的 App。这一点还须要咱们持续迭代才能作到。
整个木星计划下来,我的有不少的收获和感悟。说实话,此次的技术改造在技术上并无太大的难度,更谈不上有什么「黑科技」。改造能顺利落地,更多的对于全局的把控、资源的协调沟通以及各个兄弟团队的积极配合。因此这里抛开技术不谈,谈谈个人几点收获:规范化、流程化和全局视角。
安居客 Android App 团队在技术上过往基本都属于小团队做战,不多有跨团队、跨地域协同开发的经验,也缺乏和集团其它团队的交流,所以规范和流程一直是咱们团队所欠缺的。
在以前的小团队开发模式下,就这么一亩三分地,想怎么玩都行,怎么方便怎么来。可是这种方式一旦涉及到跨地域跨团队的协做时就会遇到瓶颈。
好比北京租房团队的需求开发完要集成进安居客,按照咱们单纯的想法,定个时间点提交代码给咱们就 OK 了,但实际状况是这种方式根本行不通。业务方何时开发完、测试完、何时交付给咱们?业务集成的交付标准是什么?平台方测试和业务方测试如何配合、如何交接?业务代码是以源码方式集成仍是 aar 方式集成等等这些都是问题,须要有一套标准化、流程化的规范来约束各方的行为,这样才能保证项目顺利上线。
像上面这样的例子还有不少,我这里就不一一列举了。
正所谓「不谋全局者,不足谋一域」,本次平台化改造过程当中个人另一个重大收获是让我意识到要以全局视角去看到问题。
前面提到咱们在规范和流程上有不少欠缺,不少问题单从自身没法解决,这促使我跳出本身的圈子来思考问题。咱们作 App 开发的同窗每每容易把视角局限于本身负责的一个页面、一个功能模块、一个业务,能站在整个 App 的角度看待问题的已经寥寥无几了,更别提跳出 App 的视角来看待问题。
但缺乏全局视角,不少事情就不能很好的完成。举个例子,假设你接到了一个优化页面响应速度的任务,若是你单从 App 的角度入手你会发现能作的颇有限,当你一通操做把 App 的优化点作完了却发现中台部门提供的 SDK 方法耗时两秒,API 返回数据耗时三秒,真的是一顿操做猛如虎,一看结果二百五。
上面提到的这个例子还只是最多见、最基础的一点。真正的全局视角是要求咱们跳出 App 的限制,去思考整个研发流程的痛点、跨团队协做上还有哪些优化空间等等,这样才能真正提高咱们的开发效率、产品性能和用户体验。就拿我业余时间里一直在作的 APM 项目来讲,若是只从 App 的角度来看,要实现对线上性能数据的采集会涉及到 Gradle Plugin、字节码、ASM、数据的采集、存储、上报、跨进程通信等等,每一项都不简单,每一项须要有必定的技术深度才能作好一个 APM 组件。那么是否是每一项都作好了,实现了一个优秀的 APM 组件就能解决线上性能问题、提高用户体验呢?显然不是!
若是你站在更高的角度来看,一个单纯的 APM 组件并无办法解决任何性能问题。咱们须要从前期开发阶段的开发规范和实现方案、QA 验收标准、上线后的性能数据采集、数据上报后的聚合、分析和报警、性能问题的处理流程和规范等各个维度来协同处理,全方位的系统的思考每个点,从更高的角度入手才能真正解决线上性能问题。
如今咱们把视角再往上拔高一个层次,站在整个技术团队的角度来看移动开发。
安居客 App 是市面上一类比较典型的互联网应用,它包含了基本的业务呈现、即时通信、视频播放、直播等面向用户的功能模块,也包含了数据采集、日志记录、网络通信等用户看不见的功能模块,同时还需持续交付平台、测试平台等等平台的支持。
早期,即便在同一家公司这些内容各个 App 都是独立的,各自本身作一套;
后来慢慢发现这种各自为战的模式效率过低,重复造轮子的现象太严重,因而有了平台化的概念。即时通信是一个平台、视频直播是一个平台、日志系统又是一个平台,持续交付、测试均是单独的平台。各个平台为各类 App 提供不同的服务,各个 App 单独对接各个平台就能够了,避免了重复造轮子。
这种平台化方案也有它本身的问题,经过前面的描述你会发现,基本上来一个新业务就要开发一个新系统,造成一个新平台,这样也会给 App 的接入带来困扰,同时不一样的平台天然会有不一样的团队,这之间的沟通协做成本是巨大的。因而更进一步把各类分散的平台统一成一个更大的平台,统一对各类 App 提供服务。
这就是整个 App 研发体系从蛮荒时代到平台化,再从平台到中台的完整进化史。
这里说的平台化和文章标题里的平台化不是同一个概念,这里的平台化是指一套系统做为一个平台为各类 App、Web、小程序等前台应用提供服务;而文章标题里的平台化是指安居客 App 做为一个平台来支持各种房产业务。
那么如今咱们再来看研发团队的组织结构,就彻底不同了。请看下图:
当咱们能站在这样一个角度看团队的组织结构、研发流程的时候,不少事就更容易理解了。好比中台部门推出了一套日志系统,各个前端团队要不要替换掉自研的埋点库,使用中台部门的服务,个人见解是固然要。让专业的团队作专业的事,中台为各个前端业务团队赋能,不管是质量上仍是效率上都会有极大的提高。同时这样也便于对各业务线的用户数据、行为作统一的聚合、分析、报警等等,而后进一步反哺业务。
这也是为何以前集团 TEG 团队推出 WMDA(58集团埋点系统)后,咱们要顶住巨大压力在安居客内部推广的缘由。
平台化改造能顺利完成并不是咱们一个团队的功劳,这得益于先后端、产品、测试、58 无线、58 房产等多个团队积极的配合,就好比前面介绍的不少技术方案都是 58 无线团队的同窗提出并开发的,所以要在这里说一声感谢,咱们从兄弟部门学到了不少。
此次平台化改造的过程当中涉及了太多的内容,其中每个点都能拿出来单独写一篇文章,因为篇幅限制并不能在文中一一详述。咱们团队在平台化方面的实践上还缺少足够的经验,我的能力也有限,未能将细节很好的一一呈现。若是你们发现文章中的错误或者实现方案上的不完美,欢迎在评论区留言交流指正。
若是你喜欢个人文章,就关注下个人公众号 BaronTalk 、 知乎专栏 或者在 GitHub 上添个 Star 吧!
- 微信公众号:BaronTalk
- 知乎专栏:https://zhuanlan.zhihu.com/baron
- GitHub:https://github.com/BaronZ88