APP太占内存怎么办?几个简单的修复将应用程序的大小缩减60%
图源:unsplash
我在巴西一家P2P网络借贷金融科技公司Mutual工作,具体的工作任务是帮助寻求公平利率的借款人与寻求高于市场回报的贷方联系起来。我们的ReactNative IOS和Android应用程序被广泛应用,在巴西国内的用户量很大,可以通过各种设备下载。
然而,我们可以通过Facebook的设备-年份-类库了解到:在给定设备型号的情况下,该库会显示出在哪一年该设备被视为高端设备。我们的大多数用户使用的是低端设备。
例如,最受用户欢迎的手机是三星Galaxy A10,虽然它在2019年3月推出,但在2013年被认为是一款高端手机。纵观所有用户的设备,其中85%在2015年或之前是高端产品。
因此,我们打算把重点放在优化应用程序上,这样即使设备配置不怎么高端,我们的用户也能获得良好的体验。
每年旗舰产品设备的百分比
我们最近仔细研究了一下应用程序的大小,在Android上的应用程序大小是26.8MB。虽然这个数字并不是很大,但绝对超过了同行的中位数,google Play控制台报告的中位数是16.3MB。
对于那些由于数据计划有限,可用磁盘空间很少甚至根本没有,不得不选择保留或卸载哪些应用程序的用户来说,程序大小可能是一个决定因素。
这一点对于Mutual尤为重要,因为借款人必须通过该应用程序支付每月的分期款项。当借款人卸载该应用程序时,他们按时还款的机率会急剧下降,直接影响投资者在公司市场上的回报。
Mutual的应用程序(26.8 MB)比同类应用程序要大得多
应用程序的大小不仅影响卸载率,而且对安装激活转化率也有很大的影响。APK的大小每增加6 MB,安装激活转换率就会降低1%。
Google Play的一篇文章很深入地谈到了这个问题(
https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2)。在低端设备使用比例较大的发展中国家,这种影响甚至更大:在新兴市场,如果应用程序的APK删减10 MB,安装激活转换率就能增加约2.5%。
每个国家的APK大小每减少10MB,安装激活转换率的增加
因此,必须在不降低用户体验的前提下,尽可能地缩小应用程序大小。我们要做的第一步是查看Android开发人员可用的官方资源。
Android App Bundle
这一部分表明缩小应用程序大小的最简单方法是尝试新的Android App Bundle(AAB)分发方法。在此之前,我们一直在编译一个可以在大多数Android设备中运行的旧Android Package(APK)文件,并将其上传到Google Play控制台来分发应用程序。
然而,AAB包只包含编译过的代码和资源。因此,上传时,通过了解其规格和CPU架构,Google Play本身就会为每种设备类型生成一个优化的APK。
图源:unsplash
所以只要对构建管道做一个简单的改变,就可以免费获得缩减程序大小的好处!
在浏览了这篇文章之后,我们修改其React Native Gradle构建脚本去运行bundleRelease,而不是当前的assembleRelease。就这样,我们有了AAB文件。
在对FastlaneConfig的supply进行了一些更深一步的修改之后,就可以直接自动上传到Play Store了,新版本将会出现在Google Play控制台上。通过这一改变,我们将已交付的APK大小缩减了9.1MB~12.4MB!
旧的APK为26.8 MB,而新的AAB为14.4至17.7 MB
但是要小心:如果在Hermes中使用React Native,那么可能要根据这个问题更新soloader依赖,否则就有可能让用户使用到一个有严重错误的应用程序。
好在我们能够通过在alpha发布轨道中的测试捕捉到这个问题。但它可能很容易通过,因为它不会在本地或建立一个APK时出现。
使用Android Size Analyzer优化资产
下一个建议是Android Size Analyzer。这是一个命令行工具,可以分析一个Android应用程序,指出可以对尺寸进行多少缩减。
在使用命令size-analyzercheck-bundle[BUNDLE]运行它之后,会收到一个可以优化的大型资产和映像列表,还包括配置ProGuard。
size-analyzer命令的输出
Proguard
Proguard是一个压缩、模糊和优化JAVA字节码的工具。因为已经了解到可能与其他Android库不兼容,所以我们还没有探索这一途径。我们正在寻找快速而容易的缩减的方法,未来可再进行优化。
大型资产
使用-d标志再次运行该命令,将得到按大小排序的每个资产的列表。由于size-analyzer工具不知道应用程序的用户流,它让我们决定哪些可以删除或动态捆绑。
按大小排序的大型资产列表
第一个也是最大的项目是React Native JavaScript包。目前还不可能拆分和动态加载,但是将在稍后看到如何缩小它。沿着建议列表进一步向下,我们看到许多大型字体(TTF)和图像(JPG和PNG)资产。
不需要的图片
内部Storybook工具中使用的四张巨大的JPG图片吸引了我们的注意力。他们给生产APK增加了额外的2MB垃圾!
在软件工程这个复杂的世界里,人人都会犯错误。我相信与同行分享这些经验时,大家都可以从这里学习到很多。如果不跟踪应用程序不断增长的规模,很可能也会犯这些失误。
字体
在迅速摆脱这些大图之后,我们不断地查看列表的其余部分。很明显,有大量的字体被捆绑。在与设计团队交谈后,他们告诉我们许多旧组件没有严格遵循排版指南。
图源:unsplash
所以我们确定了哪些组件可以被移除,哪些可以使用类似的更新字体。通过此法,我们把字体的使用从六个减少到四个。
另一个问题是字体资产大小巨大无比!每个字体大小几乎达到了670KB。这意味着四种字体占了未压缩包高达2.7MB的大小。
有一个名为FontForge的工具,可以更深入地查看和修改这些字体文件。打开后可以看到大部分的资产规模可以用扩展的西里尔文字和其他不需要的字形来解释。这些都可以被删除,因为应用程序完全是葡萄牙语。
随着这一变化,每个字体大小从670KB缩小到70KB,减少了90%!
字体中包含的一些字形的示例
删除不需要的字体并优化剩余的字体总共减少了3.8 MB,这意味着最终APK的大小减少了2 MB。
删除两个字体并优化剩余字体前后对比
优化图像
在剩下的图像中有一些是相当大的。我们通过图像优化工具(tinyjpg)优化了其中的几个,大小缩减了很多。之后,我们决定优化应用程序内使用的所有41个JPG和PNG资产。
优化的图像的前后
这使图像资产从2.5MB减少到756 KB,减少了70%。但是图像本身没有优化,在生成最终APK的过程中已经被压缩了。因此,实际上只为最终用户削减了500 KB。
在这之后,我们意识到已经耗尽了所有容易的hanging fruit optimizations。所有进一步的资产优化要么需要更多的努力,要么只会带来很小的改进。
优化React Native JavaScript bundle
已经看完了native资产,现在是时候分析JavaScript包了。这是一件特别值得优化的事情,原因有三:
· 首先,它减小了成品APK的bundle大小;
· 其次,由于JS虚拟机解析更少的代码,所以能进行更快的应用程序启动;
· 最后,也是最重要的一点是,它加快了每周通过CodePush多次发布的空中(OTA)更新的速度。
Bundle analyzer
要决定将如何减少bundle的大小,首先,需要能够看到什么占用了最多的空间。为此,我们将依靠另一个优秀的开源工具:
react-native-bundle-visualizer。在项目运行时,得到了应用程序的每个文件夹和依赖项以及它们各自大小的可视化。
Mutual前端代码库的库和文件夹的展示及其大小
应用程序包总共有5.49MB,其中57.8%来自node_modules依赖项,27.5%来自应用程序代码,其余部分是工具无法映射的。捆绑过程已经移除了未使用的代码路径,在这里看到的是应用程序实际使用的代码。即便如此,总还有改进的空间。
最大的依赖项是math.js,顾名思义,它实现了许多数学运算。我们应该不需要这种依赖项,因为在服务器中执行所有敏感的计算,然后只需将结果发送给应用程序。
仔细查看前端代码,可以发现库用于一些简单的操作。很有可能是一个同样从事过后端代码开发者出于习惯而使用它。我们迅速地从库中提取了这些方法,并将其纳入代码库,完全消除了此依赖项。这将捆绑包的大小降至4.64MB。移除一个lib之后,大小缩减了15.5%!
图源:unsplash
如前所述,我们使用Storybook独立地开发和测试组件。但是,它应该只在本地和临时环境中可用。任何最终用户都不能看到它。
正因为如此,我们使用一个环境变量来控制是否启用App的这一部分。虽然这对于限制访问有效,但捆绑程序无法知道该变量的值。由于这个限制,所有的Storybook代码最终都要放入到production bundle中去。
为了解决这个问题,将此部分的导入隔离到单个文件中。然后创建了该文件的两个版本:一个包含Storybook,另一个用于生产,只有一个虚拟组件。为了在面向生产时在这些文件之间切换,编写了一个脚本,该脚本在交换两个文件的绑定步骤之前运行。
通过这种方法,我们能够从生产中完全删除Storybook代码路径,消除node_modules依赖项以及内部配置的所有代码。
更新之后带有两个版本索引文件的storybook文件
通过这两项更改,能够将bundle大小从5.49MB缩减到4.2MB。这意味着用户将拥有更快的应用程序启动速度和更新下载速度。
bundle最终大小为4.2MB
在所有这些改进之后,我们再次将应用程序上传到了Play商店。最终的APK大小将在10.5到13.7MB之间,从最初的26.8MB减少了60%!按照Google Play团队的文章,这可以将安装激活转化率提高3.75%。
最初的APK和经过所有改进的最终AAB版本之间的比较
作为面向业务的软件工程师,我们知道对公司来说最好的决定就是积累技术债务来更快地更新迭代产品。对于像Mutual这样试图找到产品与市场契合点的初创公司来说,情况尤其如此。
但是如果不监控这个债务,可能会犯一些大错误,比如捆绑2MB的测试图片和使用一个不必要的巨大的库。这也是常见的隧道视野,快速和容易的机会优化已有的。
图源:unsplash
所以需要定期后退一步。确保不会错过对应用程序的大小、速度或任何方面的快速改进。我们只花了两天的时间来分析、规划和执行上述所有的改进,这些改进使应用程序的大小缩减了60%。
这么小的努力,解决困扰已久的难题,带来这么多实实在在的效果,这真的很惊人。