1.特点
为了尽可能减小应用的大小,您应在发布 build 中启用缩减功能来移除不使用的代码和资源。启用缩减功能后,您还会受益于两项功能, 一项是混淆处理功能,该功能会缩短应用的类和成员的名称 ; 一项是优化功能,该功能会采用更积极的策略来进一步减小应用的大小
当您使用 Android Gradle 插件 3.4.0 或更高版本构建项目时,该插件不再使用 ProGuard 执行编译时代码优化,而是与 R8 编译器协同工作,处理以下编译时任务 :
- 代码缩减(即摇树优化):从应用及其库依赖项中
检测并安全地移除不使用的类、字段、方法和属性 (可以规避 64k 引用限制 )。例如,如果您仅使用某个库依赖项的少数几个 API,那么缩减功能可以识别应用不使用的库代码并仅从应用中移除这部分代码 。 缩减代码 - 资源缩减:从封装应用中
移除不使用的资源,包括应用库依赖项中不使用的资源 。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源 。请转到介绍如何缩减资源 - 混淆:缩短类和成员的名称,从而减小 DEX 文件的大小。介绍如何对代码进行混淆处理
- 优化:
检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {}分支,则会移除 else {}分支的代码 。请转到介绍代码优化
默认情况下,在构建应用的发布版本时,R8 会自动执行上述编译时任务 。不过,您也可以停用某些任务或通过 ProGuard 规则文件自定义 R8 的行为。事实上,R8 支持所有现有 ProGuard 规则文件 ,因此您在更新 Android Gradle 插件以使用 R8 时,无需更改现有规则。
2.启用压缩、混淆和优化功能
当您使用 Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本 时,R8 是默认编译器 ,用于将项目的 Java 字节码转换为在 Android 平台上运行的 DEX 格式 。不过,当您使用 Android Studio 创建新项目时,缩减、混淆处理和代码优化功能默认处于停用状态。这是因为,这些编译时优化功能会增加项目的构建时间 ,而且如果您没有充分自定义要保留的代码,还可能会引入错误。
因此,在构建应用的最终版本(也就是在发布应用之前测试的版本)时,最好启用这些编译时任务。如需启用缩减、混淆处理和优化功能,请在项目级 build.gradle 文件中添加以下代码。
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt")),
"proguard-rules.pro"
)
}
}
...
}
然后在proguard-rules.pro文件里,编写混淆规则
指定压缩级别
-optimizationpasses 5?
# 不跳过非公共的库的类成员
-dontskipnonpubliclibraryclassmembers?
# 混淆时采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*?
#把混淆类中的方法名也混淆了
-useuniqueclassmembernames
?
#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
?
#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile
#保留行号
-keepattributes SourceFile,LineNumberTable
?
#保持所有实现 Serializable 接口的类成员
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
?
#Fragment不需要在AndroidManifest.xml中注册,需要额外保护下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment
?
# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
3.对代码进行混淆处理
混淆处理的目的是通过缩短应用的类、方法和字段的名称来缩减应用的 大小
然后在proguard-rules.pro文件里,编写混淆规则
androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
android.content.Context mContext -> a
int mListItemLayout -> O
int mViewSpacingRight -> l
android.widget.Button mButtonNeutral -> w
int mMultiChoiceItemLayout -> M
boolean mShowTitle -> P
int mViewSpacingLeft -> j
int mButtonPanelSideLayout -> K
虽然混淆处理不会从应用中移除代码,但如果应用的 DEX 文件将许多类、方法和字段编入索引,那么混淆处理将可以显著缩减应用的大小 。不过,由于混淆处理会对代码的不同部分进行重命名,因此在执行某些任务(如检查堆栈轨迹)时需要使用额外的工具。如需了解混淆处理后的堆栈轨迹,请参阅下一个部分,其中介绍了如何解码经过混淆处理的堆栈轨迹。
此外,如果您的代码依赖于应用的方法和类的可预测命名(例如,使用反射时),您应该将相应签名视为入口点并为其指定保留规则,如介绍如何自定义要保留的代码的部分中所述。这些保留规则会告知 R8 不仅要在应用的最终 DEX 中保留该代码,而且还要保留其原始命名。
3.自定义要保留的代码
在大多数情况下,如要让 R8 仅移除不使用的代码,使用默认的 ProGuard 规则文件 (proguard-android-optimize.txt ) 就已足够。不过,在某些情况下,R8 很难做出正确判断,因而可能会移除应用实际上需要的代码。下面列举了几个示例,说明它在什么情况下可能会错误地移除代码:
- 当应用通过 Java 原生接口 (JNI) 调用方法时
- 当您的应用在运行时查询代码时(如使用反射)
通过测试应用应该可以发现因错误移除代码而导致的错误,但您也可以通过生成已移除代码的报告检查移除了哪些代码。
如需修复错误并强制 R8 保留某些代码,请在 ProGuard 规则文件中添加 -keep 代码行。例如:
-keep public class MyClass
或者,您也可以为要保留的代码添加 @Keep 注解。在类上添加 @Keep 可按原样保留整个类。在方法或字段上添加该注释,将使该方法/字段(及其名称)以及类名称保持不变。请注意,只有在使用 AndroidX 注解库且您添加 Android Gradle 插件随附的 ProGuard 规则文件时,此注解才可用。有关详情,请参阅介绍如何启用缩减功能的部分。
在使用 -keep 选项时,有许多注意事项;如需详细了解如何自定义规则文件,请参阅 ProGuard 手册。问题排查部分简要介绍了移除代码后您可能会遇到的其他常见问题。
参考: https://developer.android.com/studio/build/shrink-code?hl=zh-cn https://www.guardsquare.com/manual/configuration/usage https://www.jianshu.com/p/dbe98916a21c
|