原文地址:How we reduced our React Native app size by 60% with a few simple fixes
作者:Hugo Grochau
App的大小对App的安装率和卸载率都有很大的影响。Google Play有篇很好的文章介绍减少App大小的重要性。
我们发现,App的大小每增大6MB,安装率将会下降1%.
文章中也披露了,在低端机为主流的发展中国家,这个影响更大的:
在新兴市场,Apk的大小减少10MB,将会让下载率增加约2.5%。
既然app大小会带来安装量的提升和卸载量的降低,所以我们开始在不影响用户体验的前提下,尽可能地减少我们的app大小。第一步就是去看一些安卓开发者的官方资源。
Android App Bundle
通过阅读那个页面,我们发现减少app大小最简单的方式就是去尝试新的Android App Bundle(AAB)方式发布。在那时,我们发布app还是先编译个能运行在大部分安卓设备上的apk,然后把它上传到Google Play管理平台。但是一个 AAB bundle 只包含我们的编译后的代码和资源。所以上传之后,由Google Play自己负责根据用户的设备规格和CPU架构,为每种设备类型生成一个优化后的APK。
所以我们构建方式的一个小改变,就可以减少很多APK大小?听上去太好了,不像是真的。
在看完文档之后,我们要做的只是改变React Native Gradle构建脚本去运行bundleRelease
而不是现在在assembelRelease
。就这样,我们得到了AAB文件。修改完构建流水线之后,可以自动将文件上传到Play Store,我们已经准备好了,新的精简版在Google Play控制台上显示出来了。
只通过这种方式,我们下发的Apk大小减少了9.1到12.4MB。这是真非常好用。
注意:如果你使用了React Native的Hermes,你需要照着这条issue升级你的soloader
依赖,否则有可能下发一个带有严重Bug的app给用户。庆幸地是,我们能够在灰度发布阶段测试出来这个问题。但是这个问题非常容易漏掉因为在本地或者构建apk时都不会出现。
使用Android Size Analyzer优化资源
下一个建议是Android Size Analyzer。这是一种命令行工具,可以分析Android app并且发现减少大小的机会。在运行完命令size-analyzer check-bundle [BUNDLE].aab
我们可以看到一个可优化的大资源和图片的列表。我们还被告知要配置ProGuard
Proguard
Proguard是一个压缩、混淆和优化Java字节码的工具。由于我们了解到与其他Android库可能存在不兼容的情况,因此我们还没有尝试这个方式。因为当前我们在寻找一个快速简单的方式减少大小,我们选择以后再尝试这种方式。(译者注:对于熟悉Android开发的同学来说,这个步骤是必须要做的,作者应该是纯前端开发)
Large assets
再次运行那个命令,带上-d
标识,我们将获得按大小排序的资源列表。由于size-analyzer
工具不了解我们的App用户行为,所以它让我们来决定哪些可以移除或者动态打包。
最大的一项就是React Native JavaScript bundle。目前还没有办法拆分或者动态加载它,但是稍后我们将介绍如何缩小它。在列表的下方我们看到很多大字体(TTF)和图像(JPG和PNG)资源。
不需要的图片
我们的注意力立即被吸引到四张很大的JPG图上,这是我们内部Storybook工具使用的。它们在我们的生产环境Apk里增加了额外的2MB“垃圾”。这个错误真是太尴尬了!当这种事情发生时,我们感到非常愚蠢。但是在复杂的软件工程世界中,我们都会犯错。我相信与我们的同行分享这些错误后,我们都可以从中吸取教训。如果不去分析App的大小增长,你也可能会犯这样的错误。
字体
在迅速移除了这些大的图片之后,我们继续看这个列表。非常明显地发现打包了很多的字体。与设计团队交谈后,他们告诉我们很多旧的组件没有严格遵循设计规范。所以我们确定了哪些组件可以被移除,哪些可以更新到新的字体。这样,我们将字体的使用量从6种减少到了4种。
我们注意到另一件事是,我们的字体资源非常大!他们大概每个670KB。这意味着我们的四个字体在未压缩的资源里占用了2.7MB的大小。非常感谢一个叫做FontForge的工具,让我们可以深入地了解并且修改字体文件。打开之后,我们可以看到里面有很多扩展的西里尔字母和其他不需要的符号。我们可以把他们全部移除,因为我们的App是纯葡萄牙语的。通过这个改变,我们把字体文件大小从670KB减小到70KB每个,减少了90%。
移除不需要的字体,并且把剩下的优化一遍,减少了3.8MB的大小。在最后压缩的APK大小里减少了2MB。
优化图片
看下剩下的图片,有些也非常大。我们可以把他们用TinyPng压缩,这可以减少很多的大小。我们决定优化我们app里所有的41张JPG和PNG图片资源。
这让我们把图片资源从2.5MB减少到了756KB,但是因为之前打包时会进行一些优化,所以实际在用户侧体现出来的效果是减少了500KB。
这个之后,我们发现我们已经完成了所有的简单优化步骤。未来的资源优化将是要么需要很多努力,要么是只会带来很少的优化效果。
优化React Native JavaScript bundle
现在我们完成了本机资源的优化,是时候去分析JavaScript bundle. 这个有优化非常重要有以下三个原因:首先,这将减少我们最后Apk打包的大小。其次,因为JS虚拟机可以解析更少的代码,这将让app更快启动。最后,也是最重要的,这将加速OTA升级,这个我们每周都会多次通过CodePush执行。
Bundle分析
为了决定如何减少bundle的大小,首先,我们需要能够看到什么占用了最大的空间。为此,我们将依靠另一个非常好的开源工具:react-native-bundle-visualizer。在我们的项目里运行它,我们将会看到App内的每个文件夹的和依赖关系以及他们各自的大小。
我们可以看到app bundle总大小5.49MB,其中57%来自node_modules依赖,27.5%来自程序代码,其他的内容工具无法映射了。打包过程已经移除了不需要的代码路径,所以我们看到这里应该是应用程序实际使用的代码。尽管如此,仍然总是有改进的空间。
我们最大的依赖是math.js,顾名思义,它实现了许多数学运算。我们不需要这种依赖,因为所有的敏感运算我们都是放在服务器进行的,然后发送运算结果给app。仔细查看前端代码,发现这个库被用于一些简单的计算。这很可能是一些既写前端代码也写后端代码的开发者习惯性地使用它。我们快速从库里提取这些方法并且放到我们的代码库里。从而完全移除这个依赖。这让我们的bundle大小变成4.64MB。删除一个库,我们减少了15.5%。
如前所述,我们使用Storybook来独立开发和测试组件。但是它应该仅存在于本地或者暂存环境。任何终端用户都不应该能够看到它。因此,我们使用ENVIRONMENT
变量来控制是否启用app的这部分内容。这个可以用来限制执行,但是打包程序无法知道这个变量的值。由于这个原因,所有的Storybook代码最后都打包到了我们的线上bundle里。
为了解决这个问题,我们将这部分引入隔离到一个单独的文件里。然后我们创建了为这个文件创建了两个版本:一个包含Storybook,另一个仅包含虚拟组件的,用于生产环境。为了在生产环境时切换着两个文件,我们写了一个脚本,可以在打包之前执行并且交换着两个文件。通过这种方法,我们可以在生产环境完全移除Storybook的依赖。从而,消除了node_modules依赖和每个story内部的配置代码。
通过这两个修改,我们把bundle大小从5.49MB减少到了4.2MB。意味着我们的用户可以有更快的app启动速度和更新下载。
经过所有这些改进之后,我们再次将app上传到Play Store。现在显示,我们最后的APK大小只有10.5到13.7MB。和我们之前的26.8MB相比减少了惊人的60%!正如Google Play团队的文章所诉,这意味着我们有可能将安装转化率提高3.75%.
总结
作为面向业务的软件工程师,我们知道有时候对公司来说,最好的决定是先累积技术债务以更快地开发产品。尤其是对于像Mutual这样,还在寻找合适市场的早期创业公司。但是如果不监控这些债务,可能会犯一些大错,比如把2MB的测试文件和不必要使用的庞大的库打包进来。不要让技术债务失控,并且在你面前爆炸。
从长远来看,需要快速简单地优化已有的代码。所以比较好的方式是,定期回顾一下,确定没有错过对App大小、速度或者任何方面的快速改进。我们只花了2天的时间去分析、计划和执行上述的所有改进,这使我们的app减少了60%。很难想象有什么方式能用这么少的努力带来这么多实实在在的收益。
本文首发于 一粟(https://www.zeyio.com),欢迎转载,但是必须保留本文的署名和链接。