去年秋天,我们推出了ReDex,它通过降低安卓应用的大小来提高性能。那时我们做了一些优化工作,像压缩、内联和删除无用代码等等,但当时还没有将其放到产品中去测试。现在我们决定要这么做了!

去年11月份我们发行了第一款经ReDex优化的安卓版Facebook应用,它比之前的版本小了25%,同时启动时间提高了30%。现在我们很高兴地宣布将 ReDex开源,希望这款工具不仅仅只用于Facebook,让它帮助开发者们将每款App都做得更小更快。

为安卓App瘦身提速的神器——来自Facebook的ReDex

为安卓App瘦身提速的神器——来自Facebook的ReDex - 敏捷大拇指 - 为安卓App瘦身提速的神器——来自Facebook的ReDex





1、新的优化

在之前的文章中,我们谈到过ReDex内部的一些优化。此后,我们又实现了一些新的优化来帮助产品更好地提速。




2、基于反馈驱动的class布局

我们实现提速的方法之一是优化app字节码在磁盘上的布局结构。默认情况下,dex文件中的class并不是按照运行时的行为来组织,而是按照构建时的工具所用的顺序排布。当app启动时,磁盘必须从大量文件中寻找随机分布在磁盘上的class数据,这必然导致延迟,对那些内部闪存较慢的旧设备来说尤甚。我们认识到优化class的位置可以改善字节码的加载性能。

我们使用了一种很常见的代码优化方法:反馈式优化(FDO)。为了实现FDO,首先在实验室收集运行时数据。因为冷启动性能对Fackbook用户很重要,我们在测试设备上跟踪冷启动时加载的class,然后将被加载过的class反馈到ReDex,它会将冷启动过程中用到的class放在dex的最前列。这种布局最大限度地减少了设备启动时从内存加载字节码的量。




3、接口移除

基于实现而不是接口的编程是一种很好的软件工程实践。然而,有时候一个新发布的app版本中会存在一些只有一个实现的接口。这个额外的接口需要占用额外的内存空间,消耗额外的方法引用,也会迫使我们增加额外的dex。另外,运行时调用接口方法通常比调用虚拟方法效率更低。

ReDex可以从整体的角度审视程序代码,因此它能移除不必要的接口。为了移除这些接口,我们先执行一次检查,遍历类的结构,寻找那些只有一个实现的接口。一旦找到,再遍历一次代码,重新编写代码来直接调用实现程序,然后将此时已经无用的接口删掉。




4、删除元数据

Dex文件包括了一些运行时不需要的元数据。比如,每一个class都包含定义了它的Java源文件的名称。我们将这些源文件的引用替换为已经在dex文件中出现过的字符串。由于文件名往往是描述性的,而且很长,采用这种取代优化可以节省大量的存储空间。

另外还有一类需要考虑的元数据是注释。虽然有些注释在运行时是需要的,但更多的仅仅是在分析或编译的时候使用。我们增加了一个白名单来移除那些不必要的注释。




5、性能数据

我们在实验室使用一套测试设备进行性能测试,同时也会从真实使用的设备中收集一些远程数据。实验室测试结果非常棒:在一台干净的Nexus 4上运行安卓4.4,启动时间可以从2s降到1.6s——即20%的提速。

在所有优化稳定之后,第一版ReDex于11月推出。通过使用这个版本的ReDex,我们成功将Facebook的Dex文件尺寸减小了大约25%。这意味着为在安卓设备上使用Facebook的人们节省了很大一块磁盘空间,同时,由于冷启动时系统需要抓取的数据更少,因而也具有更快的冷启动速度。

我们在真实使用设备上的实验也得到了类似结果。总体而言,人们使用Facebook冷启动的时间节省了20%。在高端手机获得加速的同时, ReDex为普通安卓设备提供了更大的改善——高达25~30%的提速。而普通手机也是世界范围内人们用于访问Facebook的主要设备。这主要是由于内存压力:那些拥有较小内存和较慢闪存的设备在这种优化中获益最多,因为这个优化的主要途径是减少从磁盘空间抓取字节码的数量。




6、为开发者设计

对我们团队来讲,只开发一个可以改善人们在安卓设备上访问Facebook的用户体验的工具是不够的,我们还希望这个工具对开发人员来说是易于使用的。我们从人机工程学开发者的角度出发设计了Redex:希望ReDex能快速迭代以便我们能够高效地测试和调整我们的优化策略。这种需求导致了如下三个设计原则

1. 它要快。快速的构建过程对开发人员很重要。不仅因为构建过程长时间的等待令人沮丧,也是由于慢速过程不利于进行新的优化设置,很难为最好的性能调整设置。我们努力优化ReDex本身——现在运行一个像Facebook那样大的app也只需要30秒。

2. 它的接口要简单。Dex字节码只是安卓APK格式的一部分。ReDex处理了从apk解包字节码的所有细节,分析并重新打包成相同的格式。这个设计使得把将ReDox添加到已有的工具链中非常容易,不需要增加新的支持脚本。

3. 它必须可配置。为了使APP更小,ReDex会删掉一些它认为可以优化的代码。但是也存在少数一些ReDex不能确定代码是否仍在使用的情况。一个是JNI:一个使用本地代码的APP可以在本地代码中实例化对象并调用方法,而字节码优化器看不到这些。另一个是反射:一个程序可以使用任意字符串来调用方法,因此ReDex不能确定这个方法是无用的。其实很多东西都不能通过JNI或反射来访问,所以我们假设它是不能优化的。但是对于这些情况,我们可以指定哪些方法不需要优化。只需要写一个JSON配置文件到ReDex,让它知道哪些东西不用管就行了。关于如何编写一个好的config文件请参考我们的在线文档。




7、开源:写一个ReDex优化

能将这项技术贡献给整个安卓社区,我们很兴奋。期待它能帮助其他app运行更快。我们编写ReDex时,很小心地将解析并产生Dex的库与优化本身分离。这是为了构建一个平台,以减少为dex写新的优化和工具时的工作量。大家可以从GitHub获取ReDex,并在你的app上试用。我们希望听到各种反馈:大家的问题和意见,尤其是关于新特征的需求。





作者:Bert Maher
译者:风小标
来源:Facebook技术博客