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 又是干嘛的?简单点说就是个高效获取新闻的网站,感兴趣的可以自己百度下。