生存or毁灭?QQ空间150万行代码的涅槃重生

生存or毁灭?QQ空间150万行代码的涅槃重生

腾小云导读

今年是 QQ 空间诞生的第十八年,空间客户端团队也在它十八岁生日前夕完结了架构晋级。因为以前不标准的多团队协同开发,导致代码逐渐劣化,有着巨大的风险。于是 QQ 空间面临巨大的前史债款,选择了重构晋级,不破不立。这儿和咱们共享一下在重构进程中遇到的问题和解题思路,欢迎阅读。

目录

1 空间重构项意图背景

2 为什么要重构

3 空间的架构是怎么崩坏的

4 架构的生命力

5 渐进式重构怎么完结

6 怎么确保架构的扩展性与复用性7 怎么下降杂乱度并长时间可控

8 怎么避免劣化

9 功能优化

10 项目重构效果总结

11 展望

18年前,QQ 空间上线,迅速风行全网,成为了许多人的芳华回忆。18年后的今日,QQ 空间的生命力依然强劲,是许多年轻用户的首选社交平台。

而作为最老牌的互联网产品之一,QQ 空间的代码也比较陈腐,代码运转环境杂乱,维护本钱高,全体架构亟需一场晋级。

01、空间重构项意图背景

作为一个平台型的进口,空间承当了为许多兄弟事务引流的职责,许多团队在空间的代码里协作开发。加上自身多年累积的功用迭代,空间的事务变得十分杂乱。事务的杂乱带来了架构的杂乱,架构的杂乱意味着维护本钱的升高。多年来空间的事务交接频频,多个团队接手。交到咱们团队手上时,空间的代码现已一言难尽。

这儿先简略介绍一下空间的事务形状:空间现在首要的进口是在手 Q 里,咱们叫做结合版。一起独立版的空间 App 还在维护(没错,空间独立 App 依然还有一批忠实观众)。能够看到,空间有一套独立于手 Q 之外的架构,结合版与独立版会共用大量技能组件和事务组件。

生存or毁灭?QQ空间150万行代码的涅槃重生

02、为什么要重构?

空间是一个祖上很阔的事务,代码量十分巨大,单统计结合版的代码,就超过了150w 行。一起空间的代码运转环境也极为杂乱,触及5个进程和2个插件。随着频频的交接和多团队的协同开发,空间的代码逐渐劣化,各项代码质量的方针简直都在手 Q 里垫底。

空间的代码成了闻名的原始森林 – 进得去出不来。代码的劣化导致前史 bug 难以收敛,即便一行代码不改,每个版别也会新增前史 bug30+。

面临如此巨大的前史债款,空间现已到了寸步难行,不破不立的境地,重构势在必行。所以,借着空间 UI 晋级的关键,空间团队开端空间前史上最大规模的一次重构。

03、空间的架构是怎么逐渐劣化的?

跳出棋局,站在今日的视点回头看,能够发现空间的代码是个典型事例,很好地展示了一个洁净的架构是怎么逐渐劣化的。

生存or毁灭?QQ空间150万行代码的涅槃重生

3.1 扩展性低,异化代码无处安放

结合版与独立版触及大量的代码复用,包含组件、页面和跨 App 的复用等。但因为前期架构扩展性不高,导致异化的事务代码无处安放,开端侵入底层技能组件。底层组件代码开端受到污染。

3.2 代码未阻隔且缺少编程范式

空间是个平台型的事务,广告、会员、游戏、直播、小国际等团队都会在空间的代码里开发。因为没有做好代码阻隔,各团队的代码耦合在一起,各写各的。一起因为缺少编程范式,同一个类中的代码风格迥异。破窗效应产生,污染开端扩散。

3.3 维护本钱暴增,恶性循环

空间的事务逻辑自身就很杂乱,代码的劣化使其杂乱度暴增,后续接手团队已有心无力,只能缝缝补补又三年,恶性循环。

最后陷入怪圈: 代码很乱可是稳定,开发道理都懂但的确不敢动。

生存or毁灭?QQ空间150万行代码的涅槃重生

3.4 Feeds 流的崩坏

以空间的 Feeds 流为例,最开端的架构思路是很清楚的,中心功用在基类完结,上层事务能够低本钱地开发一个新的 Feeds 流页面。一起做了许多动态化和容器化的规划,来满意迭代功率。

生存or毁灭?QQ空间150万行代码的涅槃重生

但后续的需求迅速胀大,异化出18种 Feeds 流场景,单 Feeds 流或许呈现60多种卡片。这导致基类代码与 Feed View 中的代码迅速胀大。一起 N 个团队在同一批代码中开发,代码行数和圈杂乱度逐渐劣化。

生存or毁灭?QQ空间150万行代码的涅槃重生

04、架构的生命力

痛定思痛,在进行空间重构前的首件事情就是总结经验,避免重蹈覆辙。怎么确保这次重构平稳落地而且避免后续每三年一重构?

咱们总结了四点:

渐进式重构:高速公路换轮胎,怎么平稳落地? 进步扩展性和复用性:是否能低本钱搬迁到其他事务,甚至是其他 App? 杂乱度长时间可控:n 个团队跑来做两年需求,杂乱度会不会变高? 做好防劣化:劣化代码被引入,能否快速发现?

空间的重构都围绕着这四个问题来进行。

05、渐进式重构怎么完结?

作为一个亿级日活的事务,空间呈现线上问题很简单引起大量投诉。高速公路换轮胎,小步快跑是最合适的方法。因而,平稳落地的关键是渐进式重构,避免步子迈得太大导致作业量扩散。

要做到渐进式重构,中心是确保两点:

一个杂乱的大问题能被分解为许多个小问题,可针对小问题重构和回滚; 系统随时都是可用状况。每处理一个小问题,都能够针对性的测验和上线。

为了完结以上两点,咱们依据以下几点来进行改造:

生存or毁灭?QQ空间150万行代码的涅槃重生

5.1 先拆解,后办理

咱们并没有立即开端对旧代码进行重写,而是先依据团队的 RFW-Part 结构对老代码进行拆解。Part 自带生命周期,能够确保老代码平移前后的运转逻辑共同。

尽管代码逻辑没有创新,但大问题被拆解为一个个小问题,咱们再依据优先级对单个 Part 进行重构。确保不管重构了多少,空间都是可用状况,能立即上线验证。

RFW-Part 结构后文会有介绍,此处不做打开。

5.2 架构交融

咱们彻底扔掉了空间老的技能组件,与团队内部沉淀的 RFWComponent 进行架构交融,一起也活跃接入手 Q 共同的 UI 系统。确保开发能专心于事务中间层开发。

5.3 提效前置,简化运转环境

在进行事务重构前,咱们先还了一部分技能债。包含去插件化、进程共同、工程结构优化和编译优化等。这些作业都在事务重构前完结并上线验证,简化了空间代码的运转环境,提高开发功率,确保了重构作业的敏捷性,到达了针对单点问题快速重构快速验证的意图。

生存or毁灭?QQ空间150万行代码的涅槃重生

06、怎么确保架构的扩展性与复用性?

扩展性和复用性是软件工程永恒的话题。空间前史架构并没有很好处理这两点,其他事务接入时难以处理异化逻辑,使异化逻辑侵入底层代码。一起为了强行完结结合版和独立版的代码复用,使不同的场景耦合在一起,互相干扰。

为了进步架构的扩展性和复用性,咱们从头规划了空间的架构层级。

6.1 事务层打薄,专心中间层

为了避免代码跨层级污染,咱们对架构的分层比以往更细,阻隔做得更严厉。

底层技能组件依据 RFW 结构。RFW 中的组件更洁净,没有任何事务侵入,能在其他 App 开箱即用。

中间层担任对 RFW 组件和手 Q 运转环境做桥接,并对底层组件进行扩展,完结一些空间相关但与详细场景无关的功用。中间层的代码能在一周之内搬迁到其他 App。

生存or毁灭?QQ空间150万行代码的涅槃重生

6.2 事务层打薄,专心中间层

RFWComponent 是一线开发在实际事务中沉淀出的一套组件库,现在由空间和小国际团队共同维护。所有组件都通过了线上事务的验证,确保了易用性和扩展性。组件也很完好,开箱即用。

最重要的是,RFW 的中心组件都可由上层注入署理完结,这使其并不依靠于手 Q 的运转环境,也避免了事务侧逻辑入侵底层代码。

现在整套架构已在空间、小国际、频道、根底等团队深度运用。空间也是第一个运用这套架构重构老代码的事务,整个进程十分省心。

生存or毁灭?QQ空间150万行代码的涅槃重生

07、怎么下降杂乱度并长时间可控?

7.1 组合代替继承,Part + Section,拆!

什么是 RFW-Part?RFW-Part 是团队内部沉淀的一套页面级的 UI 容器架构,Part 可感知页面的生命的周期,功用在内部闭环。不同 Part 无法感知对方存在,代码是严厉阻隔的。

生存or毁灭?QQ空间150万行代码的涅槃重生

可是 Part 是页面级结构,无法处理 Feeds 流列表杂乱的问题,Section 架构作为 Part 的补充,首要处理列表以及 ItemView 的拆解问题。其规划思路与 Part 结构共同。

生存or毁灭?QQ空间150万行代码的涅槃重生

依据 Part 和 Section 架构,咱们将空间的代码拆分为了一个个标准的集装箱。代码杂乱度和上手难度大大下降。新人内包入职一周便可独立开发,三天就完结了新功用此时的音讯页。

生存or毁灭?QQ空间150万行代码的涅槃重生

7.2 运用 Part 架构重塑超级页面

空间80%的流量和功用都集中在老友动态页和个人主页两个 Feeds 流页面,尽管内部已依据 mvvm 分层,但单层内的杂乱度依然过高:

生存or毁灭?QQ空间150万行代码的涅槃重生

以空间的老友动态页为例,咱们将页面不同功用的代码都拆分到一个个 Part 里,Fragment 仅作为一个容器,担任拼装自己需求的 Part。

生存or毁灭?QQ空间150万行代码的涅槃重生

终究页面被拆分为27个 Part,页面代码由6000多行减少到320行。许多 Part 能够直接拿去被其他 Feeds 流页面复用。

生存or毁灭?QQ空间150万行代码的涅槃重生

7.3 运用 Section 结构重塑 Feeds 流

通过 Part 的改造,页面级的功用都被拆分为子模块。但 Feeds 流全体作为一个 Part,杂乱度依然过高,咱们需求规划一套新的结构,对 Feeds 流中的卡片进一步拆解。

7.3.1 空间老的 Feeds 流结构

这儿先介绍一下空间老的 Feeds 流结构 – Ditto。

Ditto 结构魔改了 Android 原生的布局系统。其将一个卡片按位置分为不同 Area,每个 Area 作为一个容器。不同类型的卡片依据服务端下发的数据在 Area 内部做异化。

而每个 Area 的布局由 json 文件下发,Ditto 结构解析后运用 canvas 自绘,完结显现。

生存or毁灭?QQ空间150万行代码的涅槃重生

这套架构的优势是动态化能力强,服务端可定义恣意样式,但缺陷相同明显:

代码杂乱度继续胀大; 各事务代码耦合; 功用代码涣散,AB 测验不友好; 难以扩展。

7.3.2 优化方向

为了下降杂乱度,咱们决定按以下方向优化:

中心化 -> 去中心化; 代码物理阻隔; 内部闭环,动态开关; 拼装者模式,方便扩展。

7.3.3 Section 结构架构规划

和 Part 相同,咱们将一个卡片依照功用逻辑拆分为一个个 Section,构成一个 Section 池。不同卡片依据需求拼装自己需求的 Section 即可。

Section 的 UI、数据、事务都是内部闭环的。不同 Section 互不感知,确保了代码物理阻隔。

每个 Section 会与 ViewStub 绑定,布局能够按需加载。ViewStub 与 Section 是一对多的关系,Section 在查找 ViewStub 前会先去缓存池找,这样完结了多个 Section 修改同一个 View,确保 Section 拆得尽或许细。

生存or毁灭?QQ空间150万行代码的涅槃重生

上图中各模块的详细职责如下:

Section:某一切片的完好 UI+逻辑; ViewStub:与 Section 一对多,按需加载; Assembler:担任拼装 Section,可依据页面异化; SectionManager:绑定数据、分产生命周期; DataCenter:Feeds 相关数据在各页面间的同步; IOC 结构:操控回转,用于 Section 与页面交互。

Section 全体的结构图如下:

生存or毁灭?QQ空间150万行代码的涅槃重生

7.3.4 落地作用

依据这套 Feeds 流结构,咱们完结了前史卡片的整理和重构:

接入36种 Feed,拆分52个 Section,下线28种 Feed; 重构4个中心页面,单类代码不超过500行; 单条 Feed 开发时间缩短一半; 广告/增值团队一个版别即完结前史功用搬迁。

7.4 完善通讯规划,确保代码阻隔不被打破

Part 和 Section 之间会有许多通讯的需求,比方数据同步,不同模块交互等。为了确保代码阻隔不被打破,咱们规划了比较完善的通讯机制:

页面与 Part:ViewModel + LiveData; Part 与 Part:页面级事情,事情只在 PartHost 内部生效,无需注册与反注册; 页面与页面:DataCenter 数据同步。

生存or毁灭?QQ空间150万行代码的涅槃重生

7.5 异化逻辑抽离,杂乱度继续可控

除此之外,另一种简单打穿架构的元素是异化逻辑。比方同一张卡片在不同的页面需求显现不同作用,比方数据埋点的参数需求从页面最外层传递到 Section。针对这种跨层级通讯的场景,咱们规划了一套 IOC 结构来完结依靠注入,将异化逻辑拆分到了一个个 IOC 完结类中。

生存or毁灭?QQ空间150万行代码的涅槃重生

IOC 机制的中心是:View 树回溯 + ViewTag 存储 + 接口中心办理。咱们注册时将 IOC 完结类与 View 绑定,查找时依据 View 树来回溯,确保了 O(N) 的杂乱度,且能够跨越恣意层级。

生存or毁灭?QQ空间150万行代码的涅槃重生

过去,即便传递一个 pageId 参数,也要一层层传递:

生存or毁灭?QQ空间150万行代码的涅槃重生

现在,层级再深咱们也能够很方便拿到需求的 IOC 完结。

08、晋级计划

8.1 容灾规划

站在用户的视点,其实对重构与否并没有太大感知,用户只关怀稳定性是否有下降。如此大规模的重构,一行代码引起的溃散便能使几个月的努力功败垂成。咱们上线前的首要方针就是确保用户运用不受影响,不求有功,但求无过。

因而,咱们在上线前做了许多容灾规划,确保空间的中心功用可用性。

生存or毁灭?QQ空间150万行代码的涅槃重生

8.1.1 动态开关

咱们在空间的中间层埋了装备,能通过装备下架恣意的 Part 或 Section。事务层编写代码时不用再单独为每个小模块增加开关,只要依据结构做好细粒度的拆分即可。

8.1.2 溃散维护

一起,咱们做了溃散维护的规划,确保非中心功用溃散不会影响中心功用的运用:

溃散时进行关键词匹配,到达指定频率时禁用/降级相关功用; 主动对 Part/Section/页面/Feed 做关键词匹配,无需注册; 非必要功用可手动注册关键词,增加维护。

8.2 功能监控

一起,为了避免功能劣化,咱们做了许多功能监控。

针对线上:

利用手 Q RMonitor 结构的监控和咱们自己上报的滑动流通度方针,来监控页面全体的流通度; 通过在结构层打点,来监控每一个 Part、Section 或 Feed 的耗时。有劣化的模块引入时能快速发现; 完结 RFWTracer 结构,主动在页面发动流程中打点,统计页面发动各阶段的耗时。

针对线下:

咱们依据 ARTMethodHook 结构,完结对详细 View 耗时的监控,能快速定位到出问题的控件,节约开发定位功能问题的时间。

全体监控系统如图:

生存or毁灭?QQ空间150万行代码的涅槃重生

实际作用如图:

生存or毁灭?QQ空间150万行代码的涅槃重生

09、功能优化

第一次灰度后,咱们为难地发现发动速度并没有大幅提高,流通率甚至产生了下降。因而咱们做了首屏发动和流通度的专项优化:

9.1 首屏发动优化

咱们从头整理了发动流程中的数据处理,在发动前和发动后做了一定优化:

生存or毁灭?QQ空间150万行代码的涅槃重生

  • 布局异步渲染

咱们将首屏发动前,会依据缓存提早核算需求的布局,完结布局异步预加载。一起,为了确保 Context 的正确性,咱们 Hook 了 Activity 的发动流程,提早准备好空的 Activity 目标用于异步 inflate,并在发动后绑定实在的 Context。

生存or毁灭?QQ空间150万行代码的涅槃重生

  • 精准预加载

在首屏发动前读取缓存,提早核算首屏 Feed 对应的 Section 布局并异步加载。

  • 生命周期扩展

扩展 Part 生命周期,各个 Part 的非必须功用在首屏展示后初始化。

  • 优化后的作用

空间老友动态页的冷发动速度提高56%,热发动速度提高53%。

9.2 列表功能优化

通过剖析,咱们发现列表卡顿的原因集中在两点:

Item 复用率低,导致频频创立新 View; 布局嵌套多,丈量较慢。

处理思路:

边滑边异步 inflate:为了处理频频创立新 View 的问题,咱们在滑动时,会提早核算后边卡片所需的 ViewStub,并提早异步加载好。 自定义组件,下降层级,提早核算高度:列表中部分组件丈量功能较差,比方部分嵌套 RecyclerView 的组件,会频频触发子 RecyclerView 的丈量,拉高全体丈量耗时。对于这些组件,咱们运用自定义组件的方法进行了替换。下降布局层级,而且提早核算高度,设置布局的高度为固定值,避免频频丈量。

优化后的作用:完结优化后,空间首页 FPS 完结了反超,相比老版别提高了 4.9%。

10、项目重构效果总结

从咱们 AB 测验的实验数据来看,重构的全体结果是比较正向的,代码质量提高与功能提高带来了事务方针的提高,事务方针的提高也带来广告方针的提高。

生存or毁灭?QQ空间150万行代码的涅槃重生

11、展望

空间的代码前史悠久,错综杂乱,使得空间事务在很长一段时间都处于维护状况,难以快速开发新的需求。最大的三个模块是压在空间事务上的三座大山:Feeds 流、相册和宣布。通过这次架构晋级,咱们完结空间底层架构的焕新,彻底重写了最杂乱的 Feeds 流场景,一起相册模块也现已重构了一半。等剩下模块重构完结,空间的祖传代码就被悉数重写了。面向未来,咱们也能够更迅速地支撑新需求的落地,让十八岁的 QQ 空间焕然重生,从头上路。欢迎转发共享~

-End-

原创作者|尹述迪

生存or毁灭?QQ空间150万行代码的涅槃重生

QQ 空间已18年,你有什么关于QQ空间的回忆杀?欢迎在腾讯云开发者公众号评论,咱们将选取1则最有含义的评论(请运用加密对话),送出腾讯云开发者-棒球帽1个(见下图)。7月27日中午12点开奖。

生存or毁灭?QQ空间150万行代码的涅槃重生

生存or毁灭?QQ空间150万行代码的涅槃重生

生存or毁灭?QQ空间150万行代码的涅槃重生

生存or毁灭?QQ空间150万行代码的涅槃重生

生存or毁灭?QQ空间150万行代码的涅槃重生