ProGuard 在 Android 上的使用姿势(2),6年老Android面经总结

文章介绍了ProGuard在Android应用中的作用,包括代码压缩、混淆、去除无用代码以减小包体积,以及处理编译时依赖引发的问题。作者强调了配置ProGuard规则和测试的重要性,还提到了APKAnalyzer在分析和生成keep规则方面的辅助作用。
摘要由CSDN通过智能技术生成

为什么使用 ProGuard

ProGuard 是一个压缩、优化、混淆代码的工具。尽管有很多其他工具供开发者们使用,但是 ProGuard 作为 Android Gradle 构建过程的一部分,已经打包在 SDK 中。

当我们构建应用时,使用 ProGuard 有很多好处。有的开发者更关心混淆这块功能,对我而言最大的用处是打包时移除 dex 中的无用代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个 Android 示例应用的空间分布图,源码地址 Topeka sample app

减少包体积的好处有很多,比如增加用户黏性和满意度,提升下载速度,减少安装时间,以便在终端设备上连接用户,尤其是在新兴市场。当然,有时候您不得不限制您的应用的大小,比如 Instant App 限制大小 4 MB,此时 ProGuard 显得必不可少了。

如果以上还不足以说服您使用 ProGuard,其实移除无用代码和混淆所有名称还有其他更多的优化效果:

  • 在一些版本的 Android 设备上,DEX 代码会在安装或者运行时被编译成机器码。原始的 DEX 和优化后的机器码都会保留在设备中,所以算一下就知道:代码越少,意味着编译时间越短,存储占用越少
  • ProGuard 除了可以大幅减少代码的空间之外,还可以让所有的标识符(包、类和成员)都使用更短的名字,如 a.Aa.a.B。这个过程就是混淆。混淆通过两种方式来减少代码:让表示名称的字符串更短;在这些方法或者属性有相同的签名情况,下这些字符串更容易被复用,最终减少了字符串池的数目。
  • 使用 ProGuard 是开启资源压缩的前提条件. 资源压缩功能会移除您项目中代码没有引用到的资源文件(如图片资源,这一般是 APK 中占比最大的部分了).
  • 通过仅将您代码中实际使用的方法打包到 APK 中,移除代码会帮您避免 64K dex 方法引用问题。尤其是您引用了很多第三方库的时候,这样可以大大降低在您应用中使用 Multidex 的需求。

每个 Android 应用都应该使用代码压缩吗?我认为是的!

但是在您激动的跳起来之前,请先继续阅读下去。当您开启 ProGuard 时,在某些非常微妙的情况下会让您的应用崩溃。虽然有些错误会在构建应用时发生,您能及时发现,但是也有些错误您只能在运行时发现,所以请确保您的应用经过彻底的测试。

如何使用 ProGuard?

在您的项目中开启 ProGuard 只需简单到添加如下几行代码在您的主应用模块的 build.gradle 文件中:

buildTypes {
/* you will normally want to enable ProGuard only for your release
builds, as it’s an additional step that makes the build slower and can make debugging more difficult */

release {
minifyEnabled true
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
}
}

ProGuard 自身的配置已经在另外一个单独的配置文件中完成了。上面的代码中,我给出了 Android Gradle 打包插件中的默认配置¹,接下去我会在 proguard-rules.pro 中加入其他的配置。

在 ProGuard 官网您可以找到一个 使用手册。 在您深入研究这些配置之前,最好先大概理解 ProGuard 是如何工作的和我们为什么要指定一些额外的选项。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您也可以去观看 part of this Google I/O session Shai Barack 的教学视频。

简单来说,ProGuard 将您项目中的 .class 文件做为输入,然后寻找代码中所有的调用点,计算出代码中所有可达的调用关系图,然后移除剩余的部分(即不可达的代码和那些不会被调用的代码)。

在您读 ProGuard 手册时,您没必要看那些 输入 / 输出的部分,因为这些 Android Gradle 打包插件会替您指定输入源(您和第三方库的代码) 和 Android jar 库(您构建应用时用到的 Android 框架类)。

想要正确配置 ProGuard,最重要的就是让它知道运行时您的哪些代码不应该被移除(如果开启混淆的话,当然也要保持他们的名称不变)。当一些类和方法会被动态访问到时(如使用反射),在某些情况下,ProGuard 在构建调用图时不能正确的决定他们的「生死」,导致这些代码被错误的移除掉。当您只从 XML 资源引用您的代码会时(通常使用底层的反射),这个情况也会发生。

在一次 Android 典型的构建过程中,AAPT(处理资源的工具)会生成一个额外的 ProGuard 规则文件。它会为 Android 应用添加一些特别的 keep 规则,所以您在 Android Manifest.xml 中记录的 Activities、Services、BroadcastReceivers 和 ContentProviders 会保持不动. 这就是为什么在上面动图中 MyActivity 类没有被被移除或者重命名.

AAPT 也会 keep 住所有在 XML 布局文件使用到的 View 类(和它们的构造函数)和其他一些类,如在过渡动画资源中引用到的过渡类。 您可以在构建后直接看这个 AAPT 生成的配置文件,位置是:<your_project>/<app_module>/build/intermediates/proguard-rules/<variant>/aapt_rules.txt

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在构建时 AAPT 生成的一个示例 ProGuard 配置文件

我会在本文后面章节中讨论更多关于 keep 规则,但是在那之前我们最好先学一下在以下情况时应该怎么做:

当 ProGuard 打断了您的构建

在您可以测试是否开启 ProGuard 后所有代码在运行时都能正常工作前,您需要先构建您的应用。不幸的是,ProGuard 可能会发现一些引用的类缺失,并给予告警,导致您的构建失败。

修复这个问题的关键是仔细观察构建时输出的消息,理解这些警告的内容并定位他们。通常的途径是修正您的依赖或者在您的 ProGuard 配置中添加 -dontwarn 规则。

这些警告的一个原因就是,您的构建路径中没有加入需要依赖的 JARs,如使用了 provided (仅编译时)依赖。而有时候,在 Android 上这些代码的依赖在运行时并不会被真正的调用。让我们看一个真实的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个项目依赖 OkHttp 3.8.0 构建时的消息。

OkHttp 库在 3.8.0 版本的类中添加了新的注解(javax.annotation.Nullable)。但是因为它们使用了编译时的依赖,所以这些注解在最终构建时不会被打包进去(哪怕应用显式的依赖了 com.google.code.findbugs:jsr305),因此 ProGuard 会抱怨 缺失了这些类.

因为我们知道这些注解类在运行时不会被使用,我们可以通过在 ProGuard 配置中添加 -dontwarn 规则来安全地忽略掉这些警告,如 在 OkHttp 文档中加入这些规则

-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault

您应该经历过类似的过程,在输出消息中看到这些警告,然后重新构建直到构建通过。重要的是去理解为什么您会收到这些警告以及您在构建时是否真的缺少这些类。

现在您可能会尝试使用 -ignorewarnings 选项直接忽略所有的警告,但这通常不是个好注意。在某些情况下,ProGuard 的警告确实有助于您发现闪退的罪魁祸首和关于您配置上的其他问题

您可能需要了解一下 Progard的 notes (优先级低于警告的消息),它可以帮您发现一些反射相关的问题。虽然它不会打断您的构建,但是在运行时可能会闪退。这会在下面的场景中发生:

当 ProGuard 移除过多的类

在某些情况下,ProGuard 并不知道一个类或者方法被使用了,例如这个类仅在反射时被使用或者仅在 XML 中被引用。为了阻止这样的代码被移除或混淆,您应当在 ProGuard 配置中指定额外 keep 规则。这取决于作为应用开发者的你,需要去发现哪些部分代码有问题并提供必要的规则。

当运行时发生了 ClassNotFoundExceptionMethodNotFoundException 异常意味着您肯定缺失了某些类或者方法,也许是 ProGuard 移除了他们,又或者是因为错误配置依赖而导致无法找到他们。所以生产环境的构建(开启 ProGuard 时)一定要注重彻底的测试并正视这些错误。

您有很多选项来配置您的 ProGuard:

  • **keep **— 保留所有匹配的类和方法
  • **keepclassmembers **— 当且仅当它们的类因为其他的原因被保留时(被其他调用点引用到或者被其他的规则 keep 住),keep 住指定的一些成员
  • **keepclasseswithmembers **— 当且仅当所有的成员在匹配的类中存在时,会 keep 住 这些类和它的成员

我建议您从 ProGuard 的这篇 class specification syntax 开始熟悉,此文讨论了上述所有的 keep 规则和前一段讨论到的 -dontwarn 选项。另外这三个 keep 规则也各有一个不同的版本支持仅保留混淆(重命名),不保留压缩。您可以在 ProGuard 官网的表格看一下概览。

作为一个可选的方案来写 ProGuard 规则,您可以直接在某个不想被混淆和移除的类、方法、属性上添加 @Keep 注解。注意,如果这样做的话,您需要把 Android 默认的 ProGuard 配置加入到您的构建中。

APK Analyzer 和 ProGuard

Android Studio 集成的 APK Analyzer 可以帮您看到哪些类被 ProGuard 移除了并支持为它们生成 keep 规则。当您构建 APK 时开启了 ProGuard,那么会额外输出一些文件在 <app_module>/build/outputs/mapping/ 目录下。这些文件包含了移除代码的信息、混淆的映射关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

加载 ProGuard 映射文件到 APK Analyzer 可以看到 DEX 视图中更多的信息

当您加载了映射文件到 APK Analyzer时(点击 “Load Proguard mappings… “ 按钮), 您可以在 DEX 视图树中看到一些额外功能:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

想要了解更多关于大厂面试的同学可以点赞支持一下,除此之外,我也分享一些优质资源,包括:Android学习PDF+架构视频+源码笔记高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值