This commit is contained in:
louzefeng
2024-07-09 18:38:56 +00:00
parent 8bafaef34d
commit bf99793fd0
6071 changed files with 1017944 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
<audio id="audio" title="31 | 了解移动App的持续交付生命周期" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c3/5a/c355363bd949db8ad134f99b27fd1b5a.mp3"></audio>
你好我是王潇俊。今天我和你分享的主题是了解移动App的持续交付生命周期。
我已经和你分享完的前30个主题里介绍的都是偏向后端持续交付体系的内容。在服务端持续交付的基础上我会再用两篇文章和你聊聊移动App的持续交付。
与后端服务相比移动App的出现和工程方面的发展时间都较短所以大部分持续交付的流程和方法都借鉴了后端服务的持续交付。但是移动App因为其自身的一些特点比如版本更新要依赖用户更新客户端的行为等所以移动App的持续交付也呈现了一些独有的特点。
同时移动App的持续交付也存在一些痛点。比如没有主流的分支模型甚至产生了Android开发团队使用Gerrit这样一个独特代码管理平台和分支模型的特例又比如移动App的编译速度也随着应用越来越大变得越来越慢再比如Apple Store审核慢、热修复困难等问题。
这样总体来看移动App的持续交付体系的搭建完全可以借鉴服务端的持续交付的经验。然后再针对移动App固有的特点进行改进和优化。
因此在这个系列我只会通过三篇文章和你分享移动App持续交付体系的特色内容而对于共性的内容部分你可以再次回顾一下我在前面所分享的内容。如果你感觉哪里还不太清楚的话就给我留言我们一起讨论解决吧。
作为移动App的持续交付系列的第一篇文章我会和你一起聊聊移动App交付涉及的问题、其中哪些部分与后端服务不太一样以及如何解决这些问题打造出一套持续交付体系。
## 代码及依赖管理
**首先和后端服务一样移动App的持续交付也需要先解决代码管理的问题**
我在专栏的第四篇文章[《一切的源头,代码分支策略的选择》](https://time.geekbang.org/column/article/10858)中和你分享了各种代码分支策略比如Git Flow、GitHub Flow 和 GitLab Flow这三种最常用的特性分支开发模型的思路、形式以及作用。
对于移动App来说业界流行的做法是采用“分支开发主干发布”的方式并且采用交付快车的方式进行持续的版本发布。
关于这种代码管理方式我会在下一篇文章《细谈移动App的持续交付流水线pipline》中进行详细介绍。
**其次移动App的开发已经走向了组件化所以也需要处理好依赖管理的问题**
移动端的技术栈往往要比统一技术栈的后端服务更复杂,所以在考虑依赖管理时,我们需要多方位地为多种技术栈做好准备。比如:
针对 Android系统业界通常使用Gradle处理依赖管理的问题。Gradle是一个与Maven类似的项目构建工具。与Maven相比它最大的优点在于使用了以Groovy为基础的DSL代替了Maven基于XML实现的配置脚本使得构建脚本更简洁和直观。
针对iOS系统我们则会使用CocoaPods进行依赖管理。它可以将原先庞大的iOS项目拆分成多个子项目并以二进制文件的形式进行库管理从而实现对iOS的依赖管理。另外这种管理依赖的方式还可以提高iOS的构建速度。
除了以上两个技术栈外移动App还会涉及到H5、Hybrid等静态资源的构建、发布和管理。那么同样的我们也就需要Nexus、npm等构建和依赖管理工具的辅助。
可以说移动App的技术仍旧在快速发展中与后端服务比较成熟和统一的状态相比我们还要花费更多的精力去适配和学习新的构建和依赖管理工具。
## 项目信息管理
项目信息管理主要包括版本信息管理和功能信息管理这两大方面。
**对于移动App的持续交付来说我们特别需要维护版本的相关信息并对每个版本进行管理**
对后端服务来说,它只要做到向前兼容,就可以一直以最新版本的形式进行发布;而且,它的发布相对自主,控制权比较大。
但对移动App来说情况则完全不同了一方面它很难保证面面俱到的向前兼容性另一方面它的发布控制权也没那么自主要受到应用商店、渠道市场和用户自主更新等多方面因素的影响。
所以在移动App的持续交付中我们需要管理好每个版本的相关信息。
另外为了提高移动App的构建和研发效率我们会把整个项目拆分多个子项目而主要的拆分依据就是功能模块。也就是说除了从技术角度来看移动App的持续交付会存在依赖管理的内容外从项目角度来看也常常会存在功能依赖和功能集成的需要。所以**为了项目的协调和沟通,我们需要重点管理每个功能的信息。**
可见做好项目信息管理在移动App的持续交付中尤为重要而在后端服务的持续交付中却没那么受重视了这也是移动App的持续交付体系与服务端的一大不同点。以携程或美团点评为例它们都各自研发了MCD或MCI平台以求更好地管理项目信息。
## 静态代码检查
静态代码检查的内容,就和后端服务比较相似了。为了提高移动端代码的质量,业界也陆续提供了不少的静态代码检查方案。比如:
<li>
Clang Static Analyzer被Xcode集成但其缺乏代码风格的检查可配置性也比较差
</li>
<li>
OCLint其检查规则更多也更易于被定制
</li>
<li>
Infer是Facebook提供的一款静态检查工具具有大规模代码扫描效率高、支持增量检查等特点。
</li>
我们也可以很方便地把这些静态检查工具集成到移动App的持续交付当中去。基本做法你可以参考我在第25篇文章[《代码静态检查实践》](https://time.geekbang.org/column/article/14407)最后分享的Sonar代码静态检查的实例的内容。
## 构建管理
移动App构建管理的大体流程我们可以借鉴后端服务的做法通过代码变更触发自动的持续集成。集成过程基本遵循拉取代码、静态检查、编译构建、自动化测试以及打包分发的标准过程。
移动App和后端服务的持续交付体系在构建管理上的不同点主要体现在以下三个方面
<li>
**你需要准备 Android 和 iOS 两套构建环境**而且iOS的构建环境还需要一套独立的管理方案。因为iOS的构建环境你不能直接使用 Linux 虚拟机处理而是要采用Apple公司的专用设备。
</li>
<li>
**在整个构建过程中,你还要考虑证书的管理**,不同的版本或使用场景需要使用不同的证书。如果证书比较多的话,还会涉及到管理的逻辑问题,很多组织都会选择自行开发证书管理服务。
</li>
<li>
**为了解决组件依赖的问题,你需要特别准备独立的中央组件仓库,并用缓存等机制加快依赖组件下载的速度**。其实,这一点会和后端服务比较相像。
</li>
## 发布管理
移动App的发布管理和后端服务相比相差就比较大了。
**首先移动App无法做到强制更新决定权在终端用户**。移动App的发布你所能控制的只是将新版本发布到市场而已而最终是否更新新版本使得新版本的功能起效则完全取决于用户。这与后端服务强制更新的做法完全不同。
**其次移动App在正式发布到市场前会进行时间比较长的内测或公测**。这些测试会使用类似Fabric Beta 或者 TestFlight 这样的 Beta 测试平台,使部分用户优先使用,完成灰度测试;或者在公司内部搭建一个虚拟市场,利用内部资源优先完成内测。而且,这个测试周期往往都比较长,其中也会迭代多个版本。
**最后移动App的分发渠道比较多样**。还可能会利用一些特殊的渠道进行发布。为了应对不同的渠道的需求,比如标准渠道版本,控制部分内容,一些字样的显示等等。在完成基本的构建和打包之后,还需要做一些额外的配置替换、增删改查的动作。比如,更新渠道配置和说明等。
以上这些因素就决定了移动App与后端服务的发布管理完全不同。关于移动App的发布我会在下一篇文章《细谈移动App的持续交付流水线pipeline》中进行详细介绍。
## 运营管理
移动App发布之后还有一件比较重要的事项那就是对每个版本的运营管理。
这里讲的运营主要是指,追踪、分析和调优这个版本发布的表现和反馈。我们运营时,主要关注的内容包括:奔溃报告、区域分析、用户分析、系统资源消耗、流量消耗、响应时长、包体大小、系统监控,以及预警等。
通常,我们也会对比版本之间的这些运营指标,以判断应用是变好了,还是变坏了。
## 热修复
后端服务修复Bug的方式一般是发现Bug后可以立刻再开发一个新版本然后通过正常的完整发布进行修复。
而移动App的发布需要用户安装才能起效这就决定了它不能采用后端服务修复Bug的方式。因为这会要求用户在很短的时间内重新安装客户端这样的用户体验相当糟糕。
但是我们也无法避免Bug。所以对移动App来说我们就要通过特定的热修复技术做到在用户不重新安装客户端的前提下就可以修复Bug。这也就是我所说的热修复。
关于热修复,比如**Android系统**,主要的方式就是以下两步:
<li>
下发补丁内含修复好的class到用户手机
</li>
<li>
App通过类加载器调用补丁中的类。
</li>
其实现原理主要是利用了Android的类加载机制即从DexPathList对象的Element数组中获取对应的类进行加载而获取的方式则是遍历。也就是说我们只需要把修复的类放置在这个Element数组的第一位就可以保证加载到新的类了而此时有Bug的类其实还是存在的只是不会被加载到而已。
当然技术发展到今天我们已经无需重复造轮子了完全可以利用一些大厂开放的方案和平台完成热修复。比如百川的hHotFix、美团Robust、手机QQ空间、微信Tinker都是很好的方案。
**iOS系统方面**Apple公司一直对热修复抓的比较严。但是从iOS7之后iOS系统引入了JavaScriptCore这样就可以在Objective-C和JavaScript之间传递值或对象了从而使得创建混合对象成为了可能。因此业界产生了一些成熟的热修复方案。比如
<li>
<p>[Rollout.io](http://Rollout.io)、JSPatch、DynamicCocoa<br />
这三个方案只针对iOS的热更新。目前Rollout.io和JSPatch已经实现了平台化脚本语言用的都是JavaScript。Rollout.io除了支持OC的热更新外还支持Swift。<br />
DynamiCocoa源自滴滴目前还没开源所以我也没怎么体验过。但是它号称可以通过OC编码自动转换成JavaScript脚本这对编码来说好处多多。</p>
</li>
<li>
<p>React Native、Weex<br />
这两个方案都是跨平台的热更新方案。其中React Native是由Facebook开发的 Weex是由阿里开发的。就我个人的体验来说Weex从语法上更贴近编程思路而且还实现了平台化使用起来更加便捷。</p>
</li>
<li>
<p>Wax 、Hybrid<br />
这两个方案比较特殊。其中Wax采用的脚本语言是Lua而不是JavaScript所以比较适用于游戏而Hybrid主要面向H5Hybrid App已经被证明不是好的方案所以用户越来越少了。</p>
</li>
## 总结
今天我主要和你分享了移动App的持续交付生命周期的几个主要部分包括代码及依赖管理、项目信息管理、静态代码检查、构建管理、发布管理、运营管理以及热修复。
然后我分享了相比于后端服务移动App的持续交付体系有哪些不同的地方。比如项目信息管理、运营管理和热修复在移动App的交付过程中被提到了更重要的位置而其他几个主要过程代码、构建、发布这三部分都因为移动App开发的特性与后端服务相比有所区别这些区别也是我要在下一篇文章中和你重点分享的内容。
## 思考题
对于移动App的交付来说版本和信息管理非常重要。你所在的公司是如何管理这些信息的有哪些可以优化的可能吗
感谢你的收听,欢迎你给我留言。

View File

@@ -0,0 +1,190 @@
<audio id="audio" title="32 | 细谈移动APP的交付流水线pipeline" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/8e/f0/8e295fe1fa86813a6ccd582db6b1d0f0.mp3"></audio>
你好我是王潇俊。今天我和你分享的主题是细谈移动APP的交付流水线pipeline
在上一篇文章[《了解移动App的持续交付生命周期》](https://time.geekbang.org/column/article/23611)中我和你分享了移动App的整个交付生命周期并把移动客户端的交付与后端服务的交付方式进行了对比。从中我们发现移动App自身的特点使得其持续交付流程与后端服务存在一定的差异。
所以今天我会在上一篇文章的基础上和你分享移动App持续交付中的个性化内容。这些个性化的内容主要表现在流水线的三个重要环节上
<li>
采用与发布快车Release Train模式匹配的代码分支管理策略
</li>
<li>
支持多项目、多组件并行的全新构建通道;
</li>
<li>
自动化发布,完全托管的打包、发布、分发流程。
</li>
接下来我就从这三个角度和你详细聊聊移动App的持续交付吧。
## 发布快车模式
首先,我先和你说说什么是发布快车。
顾名思义,发布快车,就像一列由多节车厢组成的火车,每一节车厢代表一个发布版本,整个火车以一节节车厢或者说一个个版本的节奏,定期向前发车。而工程师们,则会把自己开发完成的功能集成到一节节的车厢上,这样集成在一节车厢的功能代码,就形成了一个新的版本。
如图1所示就很好地展示了发布快车的含义。
<img src="https://static001.geekbang.org/resource/image/c8/d2/c802a0f8f0cf4e57e4854b4e227918d2.png" alt="">
从这张图上,我们可以看到,每个版本(也就是每节车厢)都由多个功能组成。
关于发布快车还有三个关键点,容易被误解或者疏忽。
**第一个关键点是,并不是说所有开发的功能,都一定要集成到最近的那节车厢、最近的那个版本中**。任何功能都应该按照既定计划规划纳入到适合的那节车厢、那个版本中。这也是为什么移动端App的持续交付需要良好的信息管理的原因。
**第二个关键点是,我们必须要保证固定间隔的发车时间,每周、每两周都可以,但必须保证每个车厢到点即发**。只有这样,我们才能保证持续交付流水线的持续运行,以及不间断地产出。这里需要注意的是,对于一些特殊的、不规则的发布,我们要把它们归类到热修复的流程,而不是在发布快车中处理。
**第三个关键点是,这个过程的最终产物是可以发布到市场的版本,而不是发布到用户侧的版本**。虽然我们把这个发布模式叫作发布快车,但其实它的最终产物是可以发布的待发布版本。所以这个流程完成后的版本没有被正式发布,或出现了部分缺陷无法发布的情况是很正常的,可以被接受。我们并不需要保证每个版本都一定能发布到用户手上。
发布快车的发布模式特别是以上说的三个特性非常符合移动App对持续交付的需求分散开发定期集成控制发布。所以绝大部分的移动App团队都选择采用发布快车的发布方式。
那么,如何才能实现这个发布快车模式的真实落地呢?
<li>
选择与发布快车模式匹配的代码分支策略;
</li>
<li>
改造出与发布快车模式匹配的构建通道;
</li>
<li>
实现发布流程的全自动化。
</li>
## 选择与发布快车模式匹配的代码分支策略
首先选择一套与之匹配的代码分支管理策略否则整个发布快车的实施会非常别扭。我们先一起回顾一下专栏的第4篇文章[《一切的源头,代码分支策略的选择》](https://time.geekbang.org/column/article/10858)。
我在这篇文章中介绍的代码分支策略中Gitlab Flow与 发布快车模式的思想看上去非常接近。那我们不妨推演一下,这个分支策略是否符合我们的需要。
首先项目仓库的初始状态如图2所示。这里有一个版本V1代码仓库中有2个分支Master是集成分支Production是发布分支。
<img src="https://static001.geekbang.org/resource/image/ad/9c/adc587c19ca50248a589639e3439019c.png" alt="">
然后以V1的commit为基准建立功能分支1并进行开发如图3所示。
<img src="https://static001.geekbang.org/resource/image/00/24/006d004b8f4c2fa2d12451ff2de76524.png" alt="">
如图4所示功能分支1开发完成后合并入Master。测试通过之后形成版本V2V2就可以作为待发布的产物了。另外在形成V2之前我们可以看到另外一个功能分支2也被建立了但这个功能分支并没有被合并到Master所以不会出现在版本V2中。
<img src="https://static001.geekbang.org/resource/image/78/cb/789bece7976130a9722a0ca90acccbcb.png" alt="">
从图5中我们可以看到V2版本后又出现了一个新的功能分支3它与功能分支2并行开发。这两个功能分支合并入Master之后被同时附加到版本V3中。
<img src="https://static001.geekbang.org/resource/image/df/45/dfb010fd678e02dc9065659657c6c745.png" alt="">
正如以上的几个步骤,如果每个版本都是定时进行构建和打包,那么这样的代码分支管理模型就是一个典型的符合发布快车的物理实现了。
## 全新的构建通道
当然,为了发布快车模式的落地,我们只是建立与之配套的代码分支管理策略还远远不够,还需要有配套的构建通道。
你可能会问,发布快车模式的落地,为什么还要选择特定的构建通道呢?
我先和你说说发布快车,以及与之配套的代码分支策略的弱点都有哪些吧。
如果功能分支合入Master分支的过程缺乏校验以及必要的构建检查的话那么Production分支在进行自动定期构建时就很容易产生问题而一旦产生问题就会错过这个要定期发布的版本。
如果这只会影响到一个或少数几个功能的话,还好;但设想一下,如果你要发布一个大版本,由于某个小功能而影响了所有的其他功能,是不是就得不偿失了呢?
所以,为了高效的持续交付,我们就必须对构建通道进行一定的改造。
<img src="https://static001.geekbang.org/resource/image/ae/f4/aeb7a2ab53ecb8e2a4d9ebefa63d5bf4.png" alt="">
如图6所示我们会在功能分支合并入Master分支前增加一次构建Merge CI Service这次构建的作用是保证功能分支的集成是成功的否则不允许合并同时对于一个代码仓库来说增加的这次构建过程要保证是串行的即如果这个仓库正有一个合并构建在进行则后续的合并构建需要等待。
这个合并构建过程保证了Master分支上的任何commit随时都可以成功构建。之后再根据发布快车的要求定期启动版本构建Auto CI Service就能顺利地得到可测试版本了。
构建测试版本之后流水线还可以继续处理在production分支上打上对应的tag。
## 自动化的发布
构建通道建立之后就是发布了。我在上一篇文章中提到移动App的发布与后端服务有所区别。移动App的发布需要特别注意这两点需求
<li>
通常在发布到市场之前,会先发布内部,进行针对新功能的内测;
</li>
<li>
通常为了节省调试信息带来的额外开销内部发布会采用debug包而正式发布则采用release包。
</li>
但是从另一方面看相比于后端服务的发布移动App的发布步骤固定且逻辑相对简单。
- iOS系统的发布步骤为构建导出ipa包记录符号表备份上传至iTC
- Android系统的发布步骤为构建打包更新渠道标识签名保存mapping文件备份上传至发布点。
理解了iOS和Android系统各自的发布步骤我们就可以很容易地做到发布自动化了。
比如针对iOS的版本发布来说在构建和打包之后我们可以获取到对应的ipa包关联对应的版本信息元数据后就可以上传到内部的发布站点供QA下载测试了或者上传到Apple TestFlight进行公测当然也可以部署到App Store了。
接下来,我就和你详细说说如何做到发布的自动化。
你可以使用Fastlane等类似的工具完成整个发布过程还可以根据不同发布的渠道定义各自的lane。当然Fastlane也可以提供打包等一系列Action帮助你完成自动化。
```
lane :release do #发布到AppStore
increment_build_number #自增版本号的方法
cocoapods #更新pod
gym #打包
deliver(force: true) #发布到AppStore
end
```
这是一段最简单的Fastfile脚本。它的功能是利用Fastlane提供的Action完成了打包并发布到AppStore。另外你还可以在AppfileFastlane用来描述App基本信息的专用描述文件中定义关于App的信息。
当然你还可以按照发布流程的需求定义自己的lane和Action完成不同的操作。
```
private_lane :build do |options|
project = options[:project] #获取项目对象
build_number = project.build_number #获取项目定义的版本号
gym(
workspace: project.workspace, #编译工作空间
configuration: project.config, #编译配置
include_symbols: true, #是否包含符号
scheme: project.scheme, #编译计划
xcargs: &quot;BUILD_NUMBER=#{build_number}&quot;, #版本号
build_path: project.package_path, #编译路径
output_directory: project.package_path, #ipa包输出地址
output_name: project.ipa_name, #ipa包的名字
silent: false) # 编译Action
end
```
这段代码展示的就是用gym action构建一个自定义的、带参数的完整的构建过程了。我们可以看到这里的参数是具体的一个project对象。当然这里还有一个叫作output_directory的参数你可以利用这个参数把构建的ipa包放到内部的下载地址。
这样看移动App的自动化发布是不是很简单[https://github.com/fastlane/examples](https://github.com/fastlane/examples)这里还有更多相关的例子,你可以参考它们完成更加复杂的自动化发布。
## 总结
今天,我和你一起分享了移动客户端持续交付流水线的几个详细点:
<li>
利用发布快车的发布模式,可以有效地管理客户端的版本,保证研发工作按节奏持续向前进展;
</li>
<li>
采用带发布分支的GitLab Flow配合发布快车的模型可以使其做到物理落地
</li>
<li>
发布快车本身也有一些弊端比如对Master分支的合并检查不够严格的话会拖累项目进度因此我们采用改造构建通道的方式避免了这个问题的产生
</li>
<li>
移动App的发布有其独特的流程通常是先内测后正式发布但其流程相对固定且容易自动化。所以我的建议是实现发布的完全自动化以提高研发效率。
</li>
另外我还介绍了Fastlane这样一个工具能够帮助你快速完成自动化的实现。
当然我今天所分享的只是移App持续交付流水线的一种方式。在工程实践中不同的产品和组织往往会存在不同的流水线。
所以关于移动App的流水线并没有对错、优劣之分合适的才是最好的。
## 思考题
你所在的团队移动App的持续交付流水线有哪些点与我今天分享的内容有所不同你可以分析出是什么原因导致了这些不同吗又是否可以进行优化呢
感谢你的收听,欢迎你给我留言。

View File

@@ -0,0 +1,169 @@
<audio id="audio" title="33 | 进阶如何进一步提升移动APP的交付效率" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f5/71/f572fd11afded0441f1590f9809d1771.mp3"></audio>
你好我是王潇俊。今天我和你分享的主题是进阶如何进一步提升移动App的交付效率
通过我在前面分享的《了解移动App的持续交付生命周期》和《细谈移动App的交付流水线pipeline》两个主题你应该已经比较全面和细致地理解了移动客户端持续交付的整个过程。
当然搭建持续交付体系的最终目的是提升研发效率。所以仅仅能把整个流水线跑起来肯定满足不了你的胃口。那么今天我就再和你聊聊如何进一步提升移动App的交付效率。
## 提升交付效率的基本思路
同其他很多问题的解决方式一样提升移动App持续交付的效率也是要先有一个整体思路再具体落实。
理解了移动App的交付流水线后你很容易就能发现它其实与后端服务的交付流水线十分相似。
后端持续交付流水线包括了代码管理、环境管理、集成和编译管理、测试管理以及发布管理这五个核心过程。而与之相比移动App的运行形势决定了其在环境管理方面没有特别多的要求。
所以我们可以从代码管理、集成和编译管理、测试管理以及发布管理这四个方面来考虑问题。而将这四个方面直接对应到研发流程的话就是标准的开发、构建、测试、发布过程。因此移动App持续交付流水线的优化我们只要从这四个过程中寻找优化点即可。
**我们优化移动App持续交付体系的整体思路就是**:首先找到这四个核心过程中存在的问题或瓶颈,再进行针对性的优化,从而达到提升效率的目的。
接下来,我们就逐一击破这四个核心过程中的难题吧。
## 如何提升开发效率?
从开发人员的角度看提升效率最好的方法就是2个字解耦。落到技术实现上来说就是通过组件化形成合理的开发框架。
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程。对于移动App来说可以横向地按功能模块进行组件化也可以纵向地按照架构层次进行组件化。当然目前移动App的架构往往都已经比较复杂了所以通常都是两者混合的模式。
组件化带来的好处包括:
<li>
<p>**方便拆分代码仓库,降低分支管理难度,提高开发并行度。**<br />
在上一篇文章《细谈移动App的交付流水线pipeline》中我给出了一种适应发布快车模式的代码分支管理模型。试想一下如果一个巨大的App的所有代码都集中在同一个代码仓库中而所有的并行开发功能又都会形成一个个的功能分支的话它们之间相互的影响将是难以想象的。<br />
其实任何一个代码仓库当需要管理的并行分支超过10个时都会让人头痛。所以组件化的好处就是对整个项目进行解耦把不相干的功能组件从代码仓库这个层面进行隔离以免互相影响。</p>
</li>
<li>
<p>**组件可以多版本存在,通过依赖快速选取所需版本。**<br />
所有的组件都可以同时发布多个版本,发布的形式可以是代码包、二进制组件等等。这样做的好处是,对于组件的提供方和依赖方来说,只需要通过版本控制就能管理或者选取自己需要的组件功能,这种方式更符合编程习惯,也降低了减少沟通成本。</p>
</li>
<li>
<p>**专业分工,形成更优的组织结构。**<br />
一旦实施组件化,各种更专业的通用组建会慢慢形成(比如网络处理、图片处理、语音处理等等),而这些更专业的组件,也会渐渐地由更专业的人或团队进行开发和维护,专业的分工使得研发效率得到进一步提升。<br />
所以,组件化其实就是通过专业分工,提升了整个组织的开发效率。</p>
</li>
当然,组件化并非完美无瑕,它同时也会引起一些问题,比如:
<li>
**组件间的依赖问题**。由于多组件、多版本的存在,加之它们之间的传递依赖,所以组件化之后的依赖管理问题不容小觑。
</li>
<li>
**组件间的兼容问题**。兼容性问题,是由组件间的依赖问题引发的,由此组件的发布管理也会成为瓶颈:组件间到底要不要兼容?出现了不兼容的情况,应该怎么办?
</li>
其实,组件化带来的这些负面影响,在开发人员的维度是看不到的,往往会发生在构建阶段。还好,这些问题并不是无解的。接下来,我们就一起看看构建阶段如何解决这些问题,并提高效率吧。
## 如何提升构建效率?
从目前业界流行的处理方法来看,提升构建阶段的效率,可以从扁平化依赖管理和二进制交付两个维度解决。
**第一,扁平化依赖管理**
组件的依赖问题到底有多让人头痛。我们一起来看看图1中的组件依赖示例吧。
一个App中的两个组件B和C都依赖了组件G但依赖的却是组件G的不同版本。所以这个组件G的2个版本间就发生了冲突。
由此可见,由传递依赖带来的不确定性,是我们经常会遇见并非常讨厌的组件依赖形式。因为发现和处理的成本都很高。
<img src="https://static001.geekbang.org/resource/image/24/87/24a6ebb48588b8a3660d0e357b2db187.png" alt="" />
通常情况下一个移动App可以拆分出十几到几十个组件。大型的移动App如淘宝、美团甚至可以拆分出几百个组件。要保证这么多组件间依赖传递的准确性其难度非常大。所以为了解决这个问题业界现在普遍直接采用扁平化的依赖管理方式减少甚至去除传递依赖以此避免组件、版本冲突的问题。
<img src="https://static001.geekbang.org/resource/image/d7/bc/d7892bb8128b939b28117fa5e05bfbbc.png" alt="" />
而且这样的扁平化管理方式对于一个App版本来说更清晰、直观。
那么,实现这种扁平化的管理方式,需要具备什么前提吗。
答案,当然是需要。这个前提就是:不同组件之间,以及不同版本之间要保证可兼容。但是,你我都清楚,要想保证全部版本的完全可兼容性,其成本是巨大的。所以,在实践中,我们不会去保证所有版本的绝对兼容,而是去实现所有版本、组件间的相对兼容性。
相对兼容性是指每个组件在发布新版本时对于其所依赖的其他组件都选择组件仓库中的最新版本。这样就保证了这个组件在发布之后的兼容性。如果所有组件都可以这么做就能保证其各自都兼容。但是这个方法不是绝对的比如我们会遇到并发发布或者多个组件间引起的功能逻辑的冲突等问题所以还是需要对移动App进行集成测试。
**第二,二进制交付**
解决了组件的依赖问题之后,我们需要再考虑的问题是,如何才能提高编译速度。
传统的移动App组件集成及编译的方式如图3所示。组件先以源码的方式集成到目标项目然后对整个项目进行编译。如果组件比较多的话采用这种方式的编译时间会非常长。有时甚至要编译1个多小时。显然我们不会接受这种低效的集成与编译方式。
<img src="https://static001.geekbang.org/resource/image/1f/6d/1f35512e72205887d48021e0ab3e1b6d.png" alt="" />
所以为了加快编译速度业界通常会采用二进制交付和集成的方案。如图4所示二进制交付会优先把组件编译成二进制包再形成版本并通过组件仓库进行版本管理正如图中的组件A Lib包。在真正编译时我们只要直接链接二进制包就可以了无需再进行一次编译。
<img src="https://static001.geekbang.org/resource/image/42/4b/42ac7ab34eacf9eaea01c8db4affa84b.png" alt="" />
使用二进制包的方式可以帮我们大幅提升移动App的编译速度。而且因为有了中间交付物我们可以采用与后端服务一样的方式在本地缓存需要依赖的组件进一步加速编译过程。
**通过对开发、构建过程的优化我们已经将原来的交付效率至少提高了1倍。**
接下来我们再一起看看如何优化测试和发布流程以求移动App的持续交付体系更高效。
## 如何提升测试效率?
提高移动App测试效率的方法主要的思路有三个
<li>
<p>**代码静态扫描工具**。<br />
移动App的测试同样可以使用与后端服务一样的代码静态扫描工具。但相比之下后端服务通常使用的那些工具虽然普适性强但太重且定制的门槛也很高所以针对移动App的代码静态扫描目前多数大厂都采用自研的方式定制静态代码扫描工具。另外针对移动App开源的静态代码扫描工具如Lint等已经可以满足小团队的使用了。</p>
</li>
<li>
<p>**UI自动化测试**。<br />
这部分的关注点是成本和收益比你我都清楚UI自动化测试的脚本维护成本高导致其难以被大规模使用。所以针对重要的模块和组件有计划地使用UI自动化测试是重中之重。</p>
</li>
<li>
<p>**自动Monkey测试**。<br />
Monkey是一款非常好用的探索性测试工具可以大幅提升测试效率有效解决手工测试的盲点。iOS系统的测试由于系统限制比较多所以可以在模拟器上执行Monkey的方式。</p>
</li>
合理地利用这些测试工具和方法,就可以有效提升客户端的测试效率。
当然在测试过程中,合理地搭配监控工具,如性能监控、白屏检测等,可以起到更好的作用。
## 如何提升发布效率?
在前面两篇文章中我提到过移动App的发布流程与后端服务相比差别较大根本原因在于移动App天生具备的分批发布特性。
所以提升移动App的发布效率我们也要采用与后端服务不一样的方式。在这里我总结了提升移动App发布效率需要注意的两个问题
<li>
<p>**要注意分发的精准性**。精准性指的是,分发的目标、数量、时长,以及渠道一定要合理、有效,否则就会消耗无谓的分发成本。<br />
这里,我和你分享一个关于分发精准性的技巧。其实,说是技巧,更不如说是大家在发布过程中容易疏忽的内容。为了进行小批量的测试,通常我们都会准备一个针对性的测试用户名单。但是,你有没有想过这份名单的更新周期呢?我看到很多组织都极少更新这份名单,其实这样既对用户体验不好,也会影响测试结果。小白鼠也要时常替换的,否则就会失去实验价值。<br />
关于这份名单的更新周期,我的建议是:结合业务实际情况,尽量避免一个用户连续多次成为小白鼠。</p>
</li>
<li>
<p>**要注意分发的稳定性**。稳定性指的是,在分发的过程中,一定要做好监控数据的收集和分析,并且要考虑好风险的处理以及必要的回滚和热修复手段。<br />
这里我也和你分享一个关于稳定性的技巧。提高分发稳定性的一个方法就是减少分发时更新的内容并同时减少更新的时间。而对于移动App来说静态资源包的差分发布就是一个优化方案。<br />
比如,携程在选择静态资源包的差分发布时,就经历了这样一个优化过程:从全量包发布,到文件二进制差分,再到预差分。前两个方案都是在更新时,进行差分;而预差分则是在版本发布时,就已经做好了差分计算。与前两种方案相比,预差分的目的就是减少更新时间。但预差分的缺点是,可能要对所有要发布的版本进行差分处理,这将是一个巨大的笛卡尔积。<br />
所以,携程在经历几次尝试后,最终选择的方案是:结合全量包发布、文件二进制差分,以及预差分三种方案的特点,形成了按需差分的方案。即,先收集用户正在使用的版本,然后只做这些版本与最新版本的差分,从而减少差分处理的成本。</p>
</li>
在我看来确保每次分发的有效性以及每次分发都能达到预期就是提高移动App发布效率的一种最有效的手段。
## 总结
在了解了移动App持续交付体系的内容后你就可以自己去动手搭建一套持续交付体系了。持续交付体系搭建起来后我们需要考虑的问题就成了如何优化这个体系的流程提升这个体系的效率。为此我从开发、构建、测试和发布这四个核心流程的角度和你分享了一些实践经验
<li>
利用组件化的思想提升开发效率,但同时也会带来组件依赖及发布的问题;
</li>
<li>
利用扁平化依赖管理的方法解决组件依赖和发布的问题,同时采用二进制交付的方式,进一步提高构建效率;
</li>
<li>
合理利用静态代码扫描、UI自动化、自动Monkey等测试工具和方法进一步提升测试效率
</li>
<li>
确保分发的精准性和稳定性,是提升发布效率的有效手段。
</li>
至此通过持续交付移动App的三篇文章再结合着以前我分享的后端服务的持续交付体系的内容你完全可以自己厘清构建移动App持续交付体系的流程了也知道了如何去优化这个流程。
希望这些内容,可以开拓你的思路,能够帮助你解决实际项目中遇到的问题。如果你还有哪些不清楚的内容,欢迎你留言和我一起讨论。
## 思考题
在今天的分享中我介绍了一种扁平化依赖管理的方法。在实际工作中你是如何管理依赖和bundle的呢
感谢你的收听,欢迎你给我留言。