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 获取.
相关资源:
Android Support 包地址
material-components 仓库地址 https://github.com/material-components
来源: https://juejin.im/post/5c867bef5188257ed847934d