如何使用 Proguard 压缩 Android 代码
How do I shrink Android code with Proguard
由于我在我的应用程序中使用了很多依赖项,我达到了 65k 方法限制(我达到了 76k 方法)。我在 android.developer 上读到 proguard 用于缩小代码。
那么 - proguard 是只压缩了我的应用程序代码还是也压缩了我的依赖项的代码?使用 proguard 压缩代码时需要注意什么吗?我该怎么做?
我的Gradle构建:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "some.Path"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
configurations {
compile.exclude group: 'org.apache.xmlbeans'
}
repositories {
maven { url "https://jitpack.io" }
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
compile 'com.opencsv:opencsv:3.4'
compile 'org.apache.poi:poi:3.12'
compile 'org.apache.poi:poi-ooxml:3.12'
}
如果您通过 ProGuard 启用缩小,它也会缩小您的依赖项。
库通常 obfuscated/minified 没有 ProGuard。有些库如果被混淆,默认情况下将无法正常工作,因此您应该检查您使用的任何库,看看它们是否有任何关于 ProGuard 的文档。例如,Butterknife 有一些特殊的 ProGuard 规则,您需要包含这些规则以确保它继续正常工作。
对我来说,你应该寻找 multidex,超过 65k 限制,而不是 proguard,因为在更长的 运行 后者不是你问题的解决方案。
查看文档:https://developer.android.com/tools/building/multidex.html
如果您在 build.grade 文件中启用缩小,那么它也会缩小您的依赖项。
请记住,Proguard 可能会引入不需要的副作用。并非所有 libraries/dependencies 都可以缩小,因为 Proguard 也会混淆代码。 (即将字符串名称转换为字符串 n)并删除未使用的代码。
看看这个 Github 项目:https://github.com/krschultz/android-proguard-snippets
作为替代方案,您可以考虑使用 MultiDex。您可以在这里阅读:https://developer.android.com/tools/building/multidex.html
TL; DR:反转你的-keep
选项,除非你爱麻烦
首先:我相信,您使用 Proguard 来克服 dex 限制的选择是正确的。我不建议在任何情况下使用 multidex 支持库:它会在您的应用程序中引入多个类加载器的问题,并且可能会以许多不明显的方式适得其反。
以下是我个人有效缩小应用程序的方法:
- 选择几个您拥有的最大的第三方依赖项;
- 检查那些是否真的支持Proguard;
- 如果有,用 Proguard 缩小它们;
- 如果您仍然不适合最大方法数,请对一些剩余的依赖项执行上述步骤;
- 如果您仍然不适合,可能会重新评估一些,不 支持 Proguard,可能阅读他们的源代码以更好地了解 他们为什么不支持't,并自己对它们应用 Proguard;
- 在最坏的情况下,将 Proguard 应用于您自己的代码;
- 如果以上都没有帮助,请使用 multidex。
正在选择收缩的依赖项
在您的情况下,首先没有很多(直接)依赖项。您可能需要查看 gradlew dependencies
的输出以更好地了解您的间接依赖项,其中一些可能是应用程序总大小的最大贡献者。然后您可以继续使用 Android Arsenal 的 "Dex" 部分中列出的一些工具来了解哪些库对 dex 方法计数贡献最大。看来大家已经大概了解了,这部分就不多说了。
请记住:压缩可执行代码在某种程度上是对库内部的重要干预,因此您宁愿少压缩以避免将来出现神秘问题。如果有疑问,请从公开声明 确实 正式支持 Proguard 的库开始(在您的情况下,这将是 Android 支持库)。
请注意,"supporting Proguard" 对不同的开发人员可能有不同的含义。您可以期望 Android 支持库开发人员至少基本能够胜任,但许多其他人会像这样使用消费者 Proguard 规则:
-keep class com.example.library.** { *; }
如果你想知道,上面的配置是基于许多现实生活中的配置,例如 Square's Leak Canary Proguard 配置。它没有说明相关开发人员的整体能力,只是提醒您使用 Proguard 可能 困难 。是的,这种配置将完全防止库的缩小和混淆,除非你从源代码构建它的本地副本并从那里删除这样的 helpful consumer-proguard-rules.pro
。
正在评估 Proguard 的依赖关系
如上所示,即使是经验丰富的开发人员有时也会选择忽略 Proguard。如果 Google 搜索有关该库的信息并且它与 Proguard return 没有任何兼容性(即使他们 做 return 一些结果!)你可能不得不对 Proguard 的使用做出您自己的判断。以下是我个人的做法:
- 如果图书馆网站上有"framework"、"enterprise"、"reflection"字样,很可能是Proguard兼容性不好;
- 如果库与编译时代码生成有任何关系(a-la Butterknife、Dagger 等),请在使用 Proguard 之前三思;
- 如果库与 JNI 混淆,在对它使用 Proguard 之前再考虑几次,Google 因为它对 Proguard 有影响 即使你不缩小库本身;
- 如果有疑问,Google and/or 阅读库源代码:使用
Class.forName
以及 Proxy.getInvocationHandler
和类似的反射代码通常是不好的迹象。
提供 Android UI 组件(例如 MPAndroidChart)的库通常可以缩小,至少如果您将 getDefaultProguardFile('proguard-android.txt')
保留在 Gradle 配置.
最重要的部分
很多开发人员(包括 Proguard 开发人员自己!)会向您提供错误的建议,即从空 Proguard 配置 + 默认 Android Proguard 配置开始,并最终在必要时添加 -keep
规则。
不要那样做!!
这些建议来自于一些人,他们要么太笨以至于无法理解普通开发人员的问题(阅读:"the Proguard developer himself"),要么不知道如何正确使用 Proguard。事实上,这些误导的做法正是这个问题的许多答案警告您不要使用 Proguard 的原因:它的默认行为就像建议某人从攀登珠穆朗玛峰开始登山。
默认的 Proguard 配置将混淆、缩小和优化所有内容——您的整个应用程序具有所有依赖项,除了一些您明确排除的 类。你不希望这样,除非你对项目中的每个库和代码行有 绝对 的理解:它们如何工作和相互交互,它们在内部使用哪些技术等。
相反,您希望在尽可能小的范围内(很少有最大的库)进行最少的必要干预(缩小代码以减少 dex 方法计数),而后果最小(仅在已知 Proguard 确实有效的情况下)。这是我针对此类情况的 Proguard 配置:
-dontoptimize
-dontobfuscate
# Prints some helpful hints, always add this option
-verbose
-keepattributes SourceFile,LineNumberTable,Exceptions,InnerClasses,Signature,Deprecated,*Annotation*,EnclosingMethod
# add all known-to-be-safely-shrinkable classes to the beginning of line below
-keep class !com.android.support.**,!com.google.android.**,** { *; }
将上述规则添加到您应用的 proguard-rules.pro
,它们将仅收缩您明确允许收缩的 类。在 -keep
行的开头附加其他安全可收缩包的通配符(与上面完全相同 - !
和 .**
部分)。
作为 ProGuard 的替代方案,您可以通过关闭 ProGuard 使用 built-in Gradle shrinker,但仍然引用 ProGuard 配置。这将删除未使用的代码,但不会混淆或执行任何其他操作 "magic"。虽然只推荐用于调试版本,但如果您认为不需要混淆,我不明白为什么不能将它用于发布版本。
与 ProGuard 相比(在我看来)的主要好处是您可以避免 ProGuard 配置与代码库结构和第三方依赖项之间的紧密耦合。
build.gradle:
minifyEnabled true
useProguard false
proguardFiles ('proguard-basic.pro', getDefaultProguardFile('proguard-android.txt'))
proguard-basic.pro:
-dontwarn javax.**
-keep class com.mycompany.** { *; }
根据 3.2 版新的 Android Studio 更新,新的代码收缩器也通过将以下行添加到项目的 gradle.properties 文件中来进行混淆
添加这一行:
android.enableR8 = true
由于我在我的应用程序中使用了很多依赖项,我达到了 65k 方法限制(我达到了 76k 方法)。我在 android.developer 上读到 proguard 用于缩小代码。
那么 - proguard 是只压缩了我的应用程序代码还是也压缩了我的依赖项的代码?使用 proguard 压缩代码时需要注意什么吗?我该怎么做?
我的Gradle构建:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "some.Path"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
configurations {
compile.exclude group: 'org.apache.xmlbeans'
}
repositories {
maven { url "https://jitpack.io" }
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
compile 'com.opencsv:opencsv:3.4'
compile 'org.apache.poi:poi:3.12'
compile 'org.apache.poi:poi-ooxml:3.12'
}
如果您通过 ProGuard 启用缩小,它也会缩小您的依赖项。
库通常 obfuscated/minified 没有 ProGuard。有些库如果被混淆,默认情况下将无法正常工作,因此您应该检查您使用的任何库,看看它们是否有任何关于 ProGuard 的文档。例如,Butterknife 有一些特殊的 ProGuard 规则,您需要包含这些规则以确保它继续正常工作。
对我来说,你应该寻找 multidex,超过 65k 限制,而不是 proguard,因为在更长的 运行 后者不是你问题的解决方案。
查看文档:https://developer.android.com/tools/building/multidex.html
如果您在 build.grade 文件中启用缩小,那么它也会缩小您的依赖项。
请记住,Proguard 可能会引入不需要的副作用。并非所有 libraries/dependencies 都可以缩小,因为 Proguard 也会混淆代码。 (即将字符串名称转换为字符串 n)并删除未使用的代码。
看看这个 Github 项目:https://github.com/krschultz/android-proguard-snippets
作为替代方案,您可以考虑使用 MultiDex。您可以在这里阅读:https://developer.android.com/tools/building/multidex.html
TL; DR:反转你的-keep
选项,除非你爱麻烦
首先:我相信,您使用 Proguard 来克服 dex 限制的选择是正确的。我不建议在任何情况下使用 multidex 支持库:它会在您的应用程序中引入多个类加载器的问题,并且可能会以许多不明显的方式适得其反。
以下是我个人有效缩小应用程序的方法:
- 选择几个您拥有的最大的第三方依赖项;
- 检查那些是否真的支持Proguard;
- 如果有,用 Proguard 缩小它们;
- 如果您仍然不适合最大方法数,请对一些剩余的依赖项执行上述步骤;
- 如果您仍然不适合,可能会重新评估一些,不 支持 Proguard,可能阅读他们的源代码以更好地了解 他们为什么不支持't,并自己对它们应用 Proguard;
- 在最坏的情况下,将 Proguard 应用于您自己的代码;
- 如果以上都没有帮助,请使用 multidex。
正在选择收缩的依赖项
在您的情况下,首先没有很多(直接)依赖项。您可能需要查看 gradlew dependencies
的输出以更好地了解您的间接依赖项,其中一些可能是应用程序总大小的最大贡献者。然后您可以继续使用 Android Arsenal 的 "Dex" 部分中列出的一些工具来了解哪些库对 dex 方法计数贡献最大。看来大家已经大概了解了,这部分就不多说了。
请记住:压缩可执行代码在某种程度上是对库内部的重要干预,因此您宁愿少压缩以避免将来出现神秘问题。如果有疑问,请从公开声明 确实 正式支持 Proguard 的库开始(在您的情况下,这将是 Android 支持库)。
请注意,"supporting Proguard" 对不同的开发人员可能有不同的含义。您可以期望 Android 支持库开发人员至少基本能够胜任,但许多其他人会像这样使用消费者 Proguard 规则:
-keep class com.example.library.** { *; }
如果你想知道,上面的配置是基于许多现实生活中的配置,例如 Square's Leak Canary Proguard 配置。它没有说明相关开发人员的整体能力,只是提醒您使用 Proguard 可能 困难 。是的,这种配置将完全防止库的缩小和混淆,除非你从源代码构建它的本地副本并从那里删除这样的 helpful consumer-proguard-rules.pro
。
正在评估 Proguard 的依赖关系
如上所示,即使是经验丰富的开发人员有时也会选择忽略 Proguard。如果 Google 搜索有关该库的信息并且它与 Proguard return 没有任何兼容性(即使他们 做 return 一些结果!)你可能不得不对 Proguard 的使用做出您自己的判断。以下是我个人的做法:
- 如果图书馆网站上有"framework"、"enterprise"、"reflection"字样,很可能是Proguard兼容性不好;
- 如果库与编译时代码生成有任何关系(a-la Butterknife、Dagger 等),请在使用 Proguard 之前三思;
- 如果库与 JNI 混淆,在对它使用 Proguard 之前再考虑几次,Google 因为它对 Proguard 有影响 即使你不缩小库本身;
- 如果有疑问,Google and/or 阅读库源代码:使用
Class.forName
以及Proxy.getInvocationHandler
和类似的反射代码通常是不好的迹象。
提供 Android UI 组件(例如 MPAndroidChart)的库通常可以缩小,至少如果您将 getDefaultProguardFile('proguard-android.txt')
保留在 Gradle 配置.
最重要的部分
很多开发人员(包括 Proguard 开发人员自己!)会向您提供错误的建议,即从空 Proguard 配置 + 默认 Android Proguard 配置开始,并最终在必要时添加 -keep
规则。
不要那样做!!
这些建议来自于一些人,他们要么太笨以至于无法理解普通开发人员的问题(阅读:"the Proguard developer himself"),要么不知道如何正确使用 Proguard。事实上,这些误导的做法正是这个问题的许多答案警告您不要使用 Proguard 的原因:它的默认行为就像建议某人从攀登珠穆朗玛峰开始登山。
默认的 Proguard 配置将混淆、缩小和优化所有内容——您的整个应用程序具有所有依赖项,除了一些您明确排除的 类。你不希望这样,除非你对项目中的每个库和代码行有 绝对 的理解:它们如何工作和相互交互,它们在内部使用哪些技术等。
相反,您希望在尽可能小的范围内(很少有最大的库)进行最少的必要干预(缩小代码以减少 dex 方法计数),而后果最小(仅在已知 Proguard 确实有效的情况下)。这是我针对此类情况的 Proguard 配置:
-dontoptimize
-dontobfuscate
# Prints some helpful hints, always add this option
-verbose
-keepattributes SourceFile,LineNumberTable,Exceptions,InnerClasses,Signature,Deprecated,*Annotation*,EnclosingMethod
# add all known-to-be-safely-shrinkable classes to the beginning of line below
-keep class !com.android.support.**,!com.google.android.**,** { *; }
将上述规则添加到您应用的 proguard-rules.pro
,它们将仅收缩您明确允许收缩的 类。在 -keep
行的开头附加其他安全可收缩包的通配符(与上面完全相同 - !
和 .**
部分)。
作为 ProGuard 的替代方案,您可以通过关闭 ProGuard 使用 built-in Gradle shrinker,但仍然引用 ProGuard 配置。这将删除未使用的代码,但不会混淆或执行任何其他操作 "magic"。虽然只推荐用于调试版本,但如果您认为不需要混淆,我不明白为什么不能将它用于发布版本。
与 ProGuard 相比(在我看来)的主要好处是您可以避免 ProGuard 配置与代码库结构和第三方依赖项之间的紧密耦合。
build.gradle:
minifyEnabled true
useProguard false
proguardFiles ('proguard-basic.pro', getDefaultProguardFile('proguard-android.txt'))
proguard-basic.pro:
-dontwarn javax.**
-keep class com.mycompany.** { *; }
根据 3.2 版新的 Android Studio 更新,新的代码收缩器也通过将以下行添加到项目的 gradle.properties 文件中来进行混淆
添加这一行:
android.enableR8 = true