Apk 极限压缩(说点不一样的)

5,659 阅读8分钟

1.引言


Readhub+ 发布后,后台有人留言要源码的,还有人问 Apk 怎么压缩的。但是目前还不打算开源,所以没有源码。不过倒是可以分享一下我在压缩 Readhub+ Apk 的一点小小的心得。

关于 Apk的压缩与优化,这是一个老生常谈的话题了。大家耳熟能详的方法就有很多,比如开启混淆,压缩图片,使用 SVG,去除无用库,使用 AndResGuard 之类的。这些网上已经有太多教程了,我就不再赘述了。今天这篇文章,是想和大家分享下不那么耳熟能详的思路。

2. 思路


几年前,我也写过一篇关于 Apk压缩的文章,当时老板说要推广应用,为了方便用户下载,叫我把 Apk 弄小点,毕竟当时流量费还是挺贵的。最开始经过上面提到的那些操作后,安装包只减少了 1M 左右,优化效果并不明显,因为写代码的时候已经比较注意规范了,所以常规的优化操作效果有限。

后来,经过仔细分析了 Apk 的组成后,我发现有一个层级比较深的页面用到了地图,因为地图会引入 so 文件,就会导致 Apk 体积增加很多。所以我很机(ji)智(zei)的用 js 地图代替了原生地图,一下子 Apk 就只剩下 3M多了,这是当时那篇文章,《Android快速实现地图功能(不仅快!而且小!)》,感兴趣的可以去看下。当然,这种做法现在已经很普遍了。

3. 分析


我觉得 Apk 优化,在代码本身已经写的比较规范的情况下,常规的压缩操作带来的效果是非常有限的。如果想要做到极限压缩,那就一定要用一些"非常手段",这不仅要从技术层面考虑,还要结合产品自身的特点。比如产品针对的用户群体,产品面向的市场范围等。

拿我最近发布的 Readhub+ App来说:

  • v1.0.0 版本,Apk 大小 1.14M
  • v1.2.0 版本,Apk 大小 1.13M
  • v1.5.0 版本,Apk 大小 861K
  • v1.8.0 版本,Apk 大小 858.25K

功能虽然在一直增加,但安装包却在一直在减小。而且如果要较真的话,这仍然称不上是极限状态,因为项目中还是用到了很多三方库,如果把这些库都去掉的话,可能最终只有不到 500K 的样子。不过 800K 相较于现在动不动就好几十兆的 App 来说,简直已经是可以忽略的大小了,所以也就不打算继续在这上面耗时间了。

既然提到了,就顺便说一下吧,Readhub+ 的 v1.8.0 版本也发布了,加入了很多设置功能,由于这个不是本文主要内容,就不具体说了,我把更新日志单独写到了一篇文章里,需要的朋友可以到公众号看《 Readhub+ 更新日志》。

4.拷贝


再说回到 Readhub+ Apk 的优化上,其实说实话,我觉得也没什么太有技术含量的操作,可能只不过是你压根没往那个方面想而已。而且这个也要结合项目自身的特点,所以这篇文章更多的是希望提供一个思路而已。

为了在 v1.2.0 的基础上,继续减小 Apk 的体积,我用 Android Studio 自带的工具分析了 Apk 文件的组成。如下图所示,其中 support 包就占了很大的体积,但是包中大部分的组件我都没有用到,虽然已经开启了混淆,但是再怎么混淆,也只是减小了文件的大小,并没有完全去除文件。

于是我就想着能不能把 support 包中无用的文件去掉。开始想通过编译的手段实现,在网上搜了一圈后,发现 gradle 目前好像不具备排除 jar 包中指定类的能力,而且即便有,那也是一个巨大的工程了。所以只能通过笨办法了,拷贝 support 包中的源码。在之前的项目中,已经默认习惯了引入 v4 包和 v7 包,所以开始优化 Apk 的时候也压根没有朝这个方面想。

正如我刚开始所说,思路还是很简单的。但比较奇怪的是,当我搜索网上关于 Apk 优化的文章时,几乎没有哪篇提到这种做法的,也不知道是不是因为这种做法太 low 了,简直让很多人难以启齿?还是这压根都不能算个方法?

但尽管拷贝听起来很“无脑”,可我觉得对于没有这样做过的人来说,还是很容易走一些弯路的。因为我在拷贝的过程中,就遇到了一些问题。(肯定有人心想,连代码拷贝都能出问题?真“辣鸡”,哭笑不得.jpg)。因为 support 包中代码非常多,所以拷贝哪个版本,拷贝哪些文件,怎么拷贝都算是问题。

4.1 拷贝哪个版本

Readhub+ 拷贝的是 androidx 中的代码,因为相较于 27.0.0 和 28.0.0,甚至更早版本的 support 包来说,谷歌对 androidx 的包结构做了更加细化的区分,而且将很多 Material Design View 类组件都拆分到了一个单独的 material-components 开源仓库中,这样拷贝起来也就更加方便了。

4.2 怎么拷贝

在操作过程中,我发现拷代码其实是比较简单的,比较麻烦的是拷贝 support 包中各种 res 资源,不过这里有两种做法可以减少一些工作量。第一种是通过在 gradle 设置 sourceSets 属性,将不同包中的 res 资源区分开。第二种就是新建一个Android Module 专门用来放 support 包中的各种 res 文件。这样就不会和自己项目中的资源混起来了,后期管理和维护起来也比较方便,Readhub+ 就是用的这种方式。

4.3 拷贝哪些文件

这个就有点因人而异了,应该说是因项目而已了,如果你追求极致的小,那么尽量不要拷贝 View 类的组件了,想要什么效果可以自己手写一个。因为 support 包的 View 组件,很多都考虑了兼容问题,所以有很多代码都是为了提高对低版本的兼容性而写的。这可能对于你们的项目来说是完全多余的。

可能有些人会有疑问,这么直接拷贝代码,不利于后期的升级维护啊。这个我觉得大可放心,首先 support 包的升级频率是非常低的,加上我们拷贝的是稳定版中的代码,除非出现了致命性的 bug,不然就算不升级一般也不会有什么问题。

通过一通拷贝之后,Readhub+ 中 support 代码占用的体积减少了 46.7%,一下就减少了好几百K,这对于一个本身只有 1.2M 的应用来说,已经是相当大的瘦身了,简直就是从贾玲瘦成了林志玲!

5.还没结束


通过拷贝 support 包的代码,已经让 Apk 的体积小了不少,但还是有继续优化的空间的。这就又要提到文章开头说的,需要我们要结合产品自身的特点,进行一些定向的优化了。

比如产品的定位,投放的市场,针对的用户等。如果产品只投放到国内市场,那么我们可以在 gradle 中配置只保留 zh 这部分语言资源,这也可以减小十几 K的大小;还有,如果我们针对的是比较年轻的用户,那么在适配分辨率的时候,可以只考虑 xxhdpi 以上的设备,甚至连 logo 也可以只保留一套,在 Readhub+ 中就是这样做的。

splits {
    density {
        enable true
        exclude "mdpi", "ldpi", "hdpi", "xhdpi", "xxxhdpi"
        compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
}

当然,还有一些其他方式,这个就和项目本身有很大的关联了,所以这里也只能提供一个思路而已。还是文章开头那句话,如果你真的想让你的 Apk 变得非常小,那就一定要结合项目自身的特点去分析,看哪里还能减小体积的。

虽然后面这些操作减少的体积并不多,只有几十 K 的样子,但这是在Readhub+ 仅有 800 多 K 的基础上减小了这么多。在这个体积下,哪怕每减小 1K也都是挺不容易的了。

最后,文章看完了,可能有些人比较懵逼,我在文中反复提到的 Readhub+ App是个什么应用?这是我最近发布到酷安市场的一款三方Readhub 客户端,至于 Readhub 又是干嘛的?简单点说就是个高效获取新闻的网站,感兴趣的可以自己百度下。至于应用下载,可以在我公众号对话窗口回复:Readhub 获取。


相关资源:

推荐阅读: