如何科学的进行Android包体积优化_android 官方也是有相关的api支持分架构打包

  • 明确全部内置包或者部分内置包不内置的影响,假如内置包是 RN 的页面bundle,那给业务方两个数据基本上就能够说明影响
    • 页面bundle现下比例,假如因为本地没有内置的bundle,打开页面需要同步进行等待下载完成才能加载的话,现场下载比例是个比较有说服力的数据。
      • 线上bundle更新耗时,我们可以统计用户启动App后的一段指定时间,90分位能下载多少个bundle,50分位能下载多少个,10分位、5分位能下载多少个,来告诉业务方,老用户、新用户、老用户新登录等各种场景,到达业务页面的时候,有多少比例的用户能完成bundle更新。
  • 明确什么样的资源需要内置,同样用RN页面bundle举例,假如App的首页就是RN页面,那这玩意儿就必须内置了,假如一个页面在犄角旮旯,日pv才不到100,那就完全可以不需要内置。
  • 给出内置资源名单
  • 拿着内置名单和上面明确的不内置影响统计,找业务方拉会, 这一步最好是从上往下进行推进,而不是同级推进
2. 分架构打包

分架构打包能减少libs文件夹体积,libs文件夹里会包含不同架构的 so 库集合。

首先我们最终apk包是要上传到应用商店的,应用商店得支持双包上传。答案确实是支持,且应用商店推荐双包上传。

Android 官方也是有相关的api支持分架构打包:

splits {
        // 基于不同的abi架构配置不同的apk
        abi {
            // 必须为true,打包才会为不同的abi生成不同的apk
            enable true
            // 默认情况下,包含了所有的ABI。
            // 所以使用reset()清空所有的ABI,再使用include指定我们想要生成的架构armeabi-v7a、arm-v8a
            reset()
            // 逗号分隔列表的形式指定 Gradle 应针对哪些 ABI 生成 APK。只与 reset() 结合使用,以指定确切的 ABI 列表。
            include "armeabi-v7a", "arm64-v8a"
            // 是否生成通用的apk,也就是包含所有ABI的apk。如果设为 true,那么除了按 ABI 生成的 APK 之外,Gradle 还会生成一个通用 APK。
            universalApk true
        }
    }

这里需要注意的是,线上并不是所有的手机都支持 64位 的安装包,应用商店可以双包上传,线上灰度更新可以下发32位的安装包或者是 32/64 兼容包。

3. So 压缩

分架构打包是减少so的数量,so压缩是减少so的单个体积。

android:extractNativeLibs="true"

android:extractNativeLibs = true时,gradle打包时会对工程中的so库进行压缩,最终生成apk包的体积会减小。

但用户在手机端进行apk安装时,系统会对压缩后的so库进行解压,从而造成用户安装apk的时间变长。

若开发人员未对android:extractNativeLibs进行特殊配置,android:extractNativeLibs默认值:

  • minSdkVersion < 23 或 Android Gradle plugin < 3.6.0情况下,打包时 android:extractNativeLibs=true
  • minSdkVersion >= 23 并且 Android Gradle plugin >= 3.6.0情况下,打包时android:extractNativeLibs=false
4. 大so动态下发

我们能看到有些so库单个体积超大,放在apk里,就算能压缩,压缩后体积仍然很大,可能会占到 app体积超过 10%。针对这种情况,选择动态下发。

动态下发的so如何进行加载

我们采用ASM的方案,对代码中所有的 System.load、System.loadLibrary 进行hook,进入到我们自己的逻辑,这样我们就可以走下面流程:

  1. 下载so库
  2. 解压so库
  3. 校验so库
  4. 加载so库

这里需要注意的一点就是,当动态下发的so没有下载、解压、校验、加载完之前,如果用户进入到了相关的业务场景,必须有兜底机制。比如在样例App的场景中,使用了 opencv 库来做图片的二维码识别,当so没下载下来时,要识别二维码就会被兜底到 zxing。

而且由于我们有较好的Hook框架的封装,所以我们需要hook时,仅仅需要进行配置即可:

5. 大文件压缩优化,对内置的未压缩大文件进行,压缩文件用高压缩率的压缩算法

假如apk里有内置的大文件,可以通过对其进行压缩从而减少包体积,压缩时可以选用高压缩率的算法。

6. 代码优化
  • 去除无用代码、资源

去除无用代码我们可以用官方的Lint检查工具

  • 去除无用三方库
  • 减少ENUM的使用

每减少一个ENUM可以减少大约1.0到1.4 KB的大小,假如有10000个枚举对象,那不就减少了14M?美滋滋啊,但实际上具体还是要看项目代码情况来考虑,毕竟不是所有的项目里都有 10000 个枚举。

7. 资源优化
  • 无用资源文件清理

去除无用资源文件可以通过lint工具来做,也可以通过微信开源的 ApkChecker来完成。

图片压缩、转webp

图片压缩可以使用TinyPng,AndroidStudio也有相关插件,官方术语就是:

使用智能的无损压缩技术来减少图片文件的大小,通过智能的选择颜色的数量,减少存储的字节,但是效果基本是和压缩前一样的。

图片着色器

相同图片只是颜色不同的话,完全可以只放一个图片,在内存里操作 Drawable,完成颜色替换。

图片动态下发

如果本地有大图,且使用要求为未压缩,或者压缩之后仍然很大,可以适当的选择动态下载该图。

resources.arsc资源混淆

resources.arsc这个文件是存放在APK包中的,他是由AAPT工具在打包过程中生成的,他本身是一个资源的索引表,里面维护者资源ID、Name、Path或者Value的对应关系,AssetManager通过这个索引表,就可以通过资源的ID找到这个资源对应的文件或者数据。

通过对apk 中的resources.arsc进行内容修改,来对apk进行深度压缩。这里可以采用微信的AndResGuard方案。

8. 三方库优化
移除无用三方库

移除无用三方库需要人肉扫描 build.gradle 文件,一个一个的去检查依赖的三方库是否被我们代码所使用。

功能重复的三方库整合

特别常见的case,RN 用的图片加载库是 Fresco,客户端用的图片加载库是 Glide,他们都是用来加载图片,可以通过删除一个库,让项目依赖的库少一个。

  • 修改三方库源码,不需要的代码进行剔除

XUtils是一个工具大杂烩,但是假如我只用它来加载图片,其他工具是不是就完全无用,可以进行剔除。

9. 去除 DebugItem 包含的 debug信息与行号信息

在讲解什么是 deubg 信息与行号信息之前,我们需要先了解 Dex 的一些知识。

我们都知道,JVM 运行时加载的是 .class 文件,而 Android 为了使包大小更加紧凑、运行时更加高效就发明了 Dalvik 和 ART 虚拟机,两种虚拟机运行的都是 .dex 文件,当然 ART 虚拟机还可以同时运行 oat 文件。

所以 Dex 文件里的信息内容和 Class 文件包含的信息是一样的,不同的是 Dex 文件对 Class 中的信息做了去重,一个 Dex 包含了很多的 Class 文件,并且在结构上有比较大的差异,Class 是流式的结构,Dex 是分区结构,Dex 内部的各个区块间通过 offset 来进行索引。

为了在应用出现问题时,我们能在调试的时候去显示相应的调试信息或者上报 crash 或者主动获取调用堆栈的时候能通过 debugItem 来获取对应的行号,我们都会在混淆配置中加上下面的规则:

-keepattributes SourceFile, LineNumberTable

这样就会保留 Dex 中的 debug 与行号信息。根据 Google 官方的数据,debugItem 一般占 Dex 的比例有 5% 左右

10. ReDex

ReDex 是 Facebook 开发的一个 Android 字节码的优化工具。它提供了 .dex 文件的读写和分析框架,并提供一组优化策略来提升字节码。官方提供预期优化效果:对dex文件优化为 8%

11. R 文件瘦身

当 Android 应用程序被编译,会自动生成一个 R 类,其中包含了所有 res/ 目录下资源的 ID。包括布局文件layout,资源文件,图片(values下所有文件)等。在写java代码需要用这些资源的时候,你可以使用 R 类,通过子类+资源名或者直接使用资源 ID 来访问资源。R.java文件是活动的Java文件,如MainActivity.java的和资源如strings.xml之间的胶水

通过R文件常量内联,达到R文件瘦身的效果。

12. 可能的更多方案

除了我上面列到的一些,市面上还有一些其他的方案,有复杂的有不复杂的,有收益高的有收益低的,大家可以在掘金上搜索Android包体积优化,就能搜到大部分了,当然,在大厂里,还会有很多很极致的方案,比如:

  • 去掉kotlin生成的许多模板代码、判空代码
  • 去除布局文件里不需要的冗余内容

思想是这么个思想,大家在实操的时候,思路就是先调研方案,调研完成之后再选型。

六、基于风险收益比及成本收益比敲定最终实现方案

这一步的重点是:明确风险收益比及成本收益比

方案与方案之间是有区别的。

  • 如果一个方案能减少包体积2M,但是线上可能会崩溃,你会做吗?
  • 如果一个方案能减少包体积2M,但是开发成本要一个月,你会做吗?

这里我们在示例App的基础上,对每个手段进行仔细分析,包括:

  1. 预期效果
  2. 成本
  3. 风险

就这样,当我们制定完成我们的目标方案之后,就可以放手干了。

手段预期效果成本是否要做进度备注
重点优化项
- 缩减RN 内置bundle预期效果:177.43M -> 114.43MRN 内置bundle缩减,xxxx版本带上
- 分架构打包,64位、32位分开打包预期效果:32位:117.43M -> 71.9M 64位:117.43M -> 87.6Mxxxx
- so压缩方案预期效果:32位:71.9M -> 55.5M 64位:87.6M -> 58.3Mxxxx
- 大so文件动态下发预期效果:32位:55.5M -> 50.7M 64位:58.3M -> 51.7Mxxxx
大文件优化
- zip优化,对内置的压缩文件替换压缩算法预期针对 assets 文件针对不同类型文件选取不同高压缩率算法
代码优化 (dex文件为 15.6M)
- 去除无用代码Android Lintxxx
- 减少ENUM的使用全部代码 enum类 一共60个,就算全删了也只是减少 84kxxx每减少一个ENUM可以减少大约1.0到1.4 KB的大小
资源优化 (目前res目录大小为 6.3M,emoji目录大小为 770k)
- 无用资源文件清理Android Lintxxx用ApkChecker再跑一次
- 图片压缩、转webpTinyPngxxx
- 图片着色器xxx
- 图片动态下发主要是针对比较大的图,实际上经过TinyPng 压缩后,图片大小已经大大减小xxx
- resources.arsc资源混淆AndResGuard两年不维护,花了一小时没完全跑起来,但看到了大致优化效果,1.3M -> 920kgithub.com/shwenzhang/…
三方库优化 (dex文件为 15.6M)
- 移除无用三方库检查一下
- 移除无用三方so库
- 功能重复三方库整合
- 修改三方库源码,不需要的代码进行剔除

七、确定优化效果

当我们进行了一系列的或大或小的改动之后,如何描述最终优化效果?给两张对比图不就行了,无图言X。

八、总结

大家在进行一些有挑战性或者是比较有意义的项目时,其实可以多进行总结,总结的好处有什么我就不多解释了,懂的都懂哈。

比如我们这里可以装模作样的这样总结一下:

做的好的方面
  1. 足够系统化
  2. 前置调研足够充分
  3. 风险、收益、成本考虑足够充分
  4. 各方面沟通足够充分
  5. 优化决心足够大
也可以告诉自己及读者几句话
  1. 这是一个系统的需要持续去投入人力的事情,万万不可有了一定结果之后放松警惕
  2. 别人能做的,我们也能做,只要有足够的决心去做
  3. 做事不能太讲究所谓的方法论,不然会掉入陷阱,但是确实要讲究方法论
  4. 有些事情你做好了,可能仅仅是因为做这个事情的人是你,如果是别人来做,也能将这件事情做好

九、展望

一般来说,进行总结之后,都得来一些展望,给未来的自己挖点坑,给总结的读者画点饼。比如我们这里就可以这样继续装模作样的展望一下:

上面已经反复提及了,当前这一期的优化工作,重点考量的指标是风险收益比及成本收益比,所以一些极致的或者成本收益比较高的优化手段并没有被采用,所以后续还是有很多事情可以深入的干下去。

  1. resources.arsc资源混淆
  2. 去除 DebugItem 包含的 debug信息与行号信息
  3. ReDex
  4. R 文件瘦身
  5. So unwind 优化
  6. kotlin相关优化

十、真正的总结

这里我就发散性的随便总结下吧。。。也不深入纠结了。

  1. 包体积优化是个庞大的工程项目,不仅仅需要优化,还需要防劣化,优化过程中还会涉及到业务冲突,说白了就是某些东西从APK包中移除了,或多或少会有些影响,还需要去跟业务方达成意见一致。
  2. 大家不管在做什么优化课题时,最好是分步骤分工期的去进行,不要一口吃成胖子,如果上来就追求完全极致的优化效果,往往会带来两个负面风险:1) 优化工期拉长,时间成本成倍增加,2)可能影响线上App或者线下各种工具的运行稳定性。

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

下面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题全套解析,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,下面只是以图片的形式给大家展示一部分。

image

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

片转存中…(img-MOROpws9-1714600384602)]

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

[外链图片转存中…(img-aAfXnItG-1714600384603)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值