一. 背景
自 18 年以来的市场环境大家都知道,各大公司都在缩减开支、提升组织效率,希望用更少的投入带来更多的产出,以此来应对经济下行带的不确定性。在这个大背景下,集团在 18 年底做出了一系列人效提升、业务整合的动作。
拿房产业务举例:58 App 里的房产业务之前是由北京的房产团队开发的,而整个安居客 App 是由我所在的上海安居客团队开发的。在这次调整之后,北京的房产团队作为一条垂直业务线来负责 58 App 和安居客 App 中的租房业务的开发,安居客团队则继续负责整个安居客 App(包含新房、二手房、内容、IM 等业务)的开发,同时还要开发 58 App 中二手房、新房、房产大内容等业务。
这样一次调整给我们带来了三大挑战:
- 在人力不变,工作量翻倍的情况下,我们要如何保证业务的迭代速度不受影响?
- 由以往的「单兵作战」变成了跨团队跨地域协作开发,我们要如何作为一个平台方来和业务方协作开发?
- 对于 58 App 而言我们是业务方,对于北京房产团队而言我们是平台方,我们要如何同时处理好双重角色?
面对这些挑战我们需要转变开发思维、改进项目架构。
**一次项目重构和架构升级,不只是要解决当下的问题,更要为未来一到两年的业务发展提供支持。**为了解决这一系列的问题,我们联合 58 无线团队、58 房产团队、前端、后端多个团队的同学一起发起了「木星计划」,也开启了我们的平台化转型之路。
二. 问题、挑战及解决方案
当公司的业务调整政策下来以后,团队面临的最急迫的一个选择是继续在 58 App 上维护老的房产代码,迭代新功能;还是将安居客现有业务搬迁到 58 App 实现一套代码在两个 App 里运行呢?
-
前者我们需要分一半的人力去开发 58 App,这必然导致团队的需求消化量减半,业务迭代速度受影响。好处是暂时不用做任何技术上的改造。
-
后者需要我们协调 58 无线、58 房产、前后端等多个团队一起对 App 做一次「大手术」,同时还需要说服产品团队容忍改造期间业务迭代速度的放缓。好处是能够一次性将安居客上的房产业务搬迁到 58 App 上,后续我们只需针对房产业务做一次开发就能跑在两个 App 上,用一份人力做了两份的活,开发效率翻倍。
为长远计,我们最终选择了后者。可是要做到一套代码双端运行并不容易,58 App 和安居客 App 隶属于北京、上海两个不同的团队开发,大家底层库不一样,技术方案不一样,开发模式也不一样,要达到我们的目的必然要费一番功夫。接下来我从整体到局部逐步介绍我们在平台化演进过程中的设计思路、遇到的问题和解决方案
2.1 整体设计
所谓平台化,就是安居客 App 要作为一个平台来对平台上承载的各种垂直业务提供服务,每个服务都需要对上层提供标准化的接口来支撑平台上各类垂直业务的功能。
这一次的项目重构除了要转型平台型 App 支持其他业务的接入和未来业务的发展,还有更重要一点是要做到一套代码在 58 和安居客双平台运行。
要做到这一点,在现有的业务体系和代码体量下虽然工作量巨大,但大的思路确是很清晰简单的:
- 底层库能统一的尽量统一;
- 短时间内无法统一的库以及平台特性通过中间层屏蔽平台差异。
安居客 App 架构调整
针对安居客 App ,我们需要调整架构,引入一个平台中间层并针对平台中间层接口做安居客 App 侧的实现,并将垂直业务中原本调用平台接口的地方改为调用中间层接口。同时将之前的业务组件层细分为「平台级组件」和「业务级组件」,所有垂直业务不再依赖平台级组件,只依赖平台中间层和业务级组件,并明确业务代码迁移的边界。
#### 58 App 架构调整
同时 58 无线团队的同学也改进了他们的架构,和我们一样引入了平台中间层及中间层在 58 App 上的实现,保证安居客业务迁移进来后能正常编译运行。
上面这两张架构图呈现了两个 App 架构调整的大方向,具体做了哪些、遇到了哪些问题我在下面的小节里一一介绍。
2.2 平台中间层屏蔽底层差异
计算机领域的任何问题都可以添加一个中间层来解决。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 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);
2.3 统一 HybridSDK 解决双平台 H5 交互问题
安居客 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,过渡阶段三套协议并存,来不及调整的旧业务使用旧协议,新开发及本次要调整的业务使用新协议。
然后随着业务的迭代,不断废弃两个平台的自有协议,最终走向统一。
2.4 统一路由协议、API 动态下发路由解决页面交互问题
现有 App 内的页面跳转要么是 intent 跳转,要么是写死的路由跳转。在这次的平台化改造过程中,我们从 58 App 上也学到了很多东西,其中动态路由下发就是我们学习并引入到安居客 App 中的。
简单的说就是 App 给各个页面定义好路由协议,App 在调用 API 的时候返回结果中会包含当前页面跳转到下一页面的路由协议,这就是所谓的动态路由下发。这样做会带来三个好处:
- 可以做到完善的降级策略,比如当线上的房源详情页出现了大面积的 Crash,API 可以返回一个 H5 页面的路由协议,临时用 H5 的房源详情页替代;同时 App 上线 HotFix,等 HotFix 覆盖率达到一定程度后 API 再改回下发Native 路由,跳回 Native 页面;
- 可以支持新功能的效果验证,利用 H5 和 Native 自由切换这一特性,可以在不发版的情况下验证新功能的数据效果。比如要上线验证某个改动或者某个新功能的效果,之前需要 App 发版,现在只需要做一版 H5 页面,API 直接路由到这个 H5,如果验证下来效果好则可以开发对应的 Native 页面,不好则可以再尝试其他方案;
- 可以支持平台化改造过程中的灰度上线,这一点放到下一小节详细说明。
2.5 灰度策略保证上线后的稳定性
我们这次的平台化改造,无论对安居客 App 还是 58 App 来说都是一次「大手术」,术后能否保证线上的稳定性、业务数据不受影响是非常重要的。就拿把安居客业务迁移到 58 App 这件事来说,如果一股脑的用安居客迁移过去的房产业务代码替代 58 App 内的房产业务代码,就算我们在技术上做到了绝对的稳定,也难保业务数据不会受影响。
好在得益于上面提到的动态路由下发方案,我们可以做到在少量城市、少量用户上做灰度,让这部分人先试用安居客迁移过去的业务,其它用户继续使用老的房产业务,数据效果好再逐步加量,直到完全替代。这样就能最大限度的降低影响,保证上线后的稳定性。
2.6 业务平移带来的包大小问题
虽然在上一次的模块化/组件化改造过程中我们对各项垂直业务做了拆分、解耦,但是各业务还是有很多重叠的业务,于是我们将这些业务下沉到 CommonBusiness 组件中,同时这个 CommonBusiness 组件里还包含了一些 App 平台级别的基础功能,因此这个 CommonBusiness 组件成了个大而全的东西。
在这次的架构调整中,我们为了实现业务的快速平移,将 CommonBusiness 和新房、二手房等几条垂直业务线的代码一股脑的迁移进了 58 App,这就直接导致了 58 App 体积的快速膨胀。于是在后期,我们将 CommonBusiness 按能力拆分成了多个独立的组件,并且分为了「平台级组件」和「业务级组件」。平台级组件属于安居客平台特有,不随业务迁移;业务级组件属于多个垂直业务公用的组件,随业务代码一起迁移到 58 App。这一点在前面的架构图中有体现。
2.7 持续演进
最后
我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
链图片转存中…(img-2s2giqAQ-1715256344673)]
[外链图片转存中…(img-RqUrezYf-1715256344674)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!