【问题标题】:How do I shrink Android code with Proguard如何使用 Proguard 缩小 Android 代码
【发布时间】:2015-07-19 23:56:35
【问题描述】:

由于我在我的应用程序中使用了许多依赖项,我达到了 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'
}

【问题讨论】:

  • im reaching the 65k Methode Limit。请发布您的build.gradle

标签: android android-proguard dex-limit


【解决方案1】:

TL; DR: 颠倒你的-keep 选项,除非你喜欢麻烦


首先:我相信,使用 Proguard 克服 dex 限制,您做出了正确的选择。我不建议在任何情况下使用 multidex 支持库:它会在您的应用程序中引入多个类加载器的问题,并且可能会以许多不明显的方式适得其反。

这是我个人有效缩小应用程序的方法:

  • 选择您拥有的几个最大的第三方依赖项;
  • 检查那些真的是否支持 Proguard;
  • 如果有,用 Proguard 缩小它们;
  • 如果您仍然不符合最大方法数,请针对剩余的一些依赖项执行上述步骤;
  • 如果您仍然不适合,可能会重新评估一些,支持 Proguard,可能阅读他们的源代码以更好地了解他们为什么不支持,并自己对它们应用 Proguard;
  • 在最坏的情况下,将 Proguard 应用于您自己的代码;
  • 如果以上任何方法都没有帮助,请使用 multidex。

选择依赖项进行收缩

在您的情况下,首先没有很多(直接)依赖项。您可能想查看gradlew dependencies 的输出以更好地了解您的间接依赖关系,其中一些可能是应用程序总大小的最大贡献者。然后,您可以继续使用Android Arsenal 的“Dex”部分中列出的一些工具来了解哪些库对 dex 方法计数的贡献最大。您似乎已经有了大致的了解,所以我不会过多地讨论这部分。

记住:压缩可执行代码对库内部的干预有点不重要,因此您宁愿减少压缩量以避免将来出现神秘问题。如果有疑问,请从公开声明它们确实正式支持 Proguard 的库开始(在您的情况下,这将是 Android 支持库)。

请注意,“支持 Proguard”对于不同的开发人员可能意味着不同的东西。您可以期望 Android 支持库开发人员至少基本能胜任,但许多其他人会附带这样的消费者 Proguard 规则:

-keep class com.example.library.** { *; }

如果您想知道,上面的配置是基于许多现实生活中的配置,例如Square's Leak Canary Proguard 配置。它没有说明相关开发人员的整体能力,只是提醒您使用 Proguard 可能困难。是的,这种配置将完全防止库的收缩和混淆,除非您从源代码构建它的本地副本并从那里删除这样的 helpful consumer-proguard-rules.pro

评估 Proguard 的依赖关系

如上所示,即使是经验丰富的开发人员有时也会选择忽略 Proguard。如果 Google 搜索有关该库并且它与 Proguard 的兼容性没有返回任何结果(即使他们确实返回了一些结果!)您可能必须对 Proguard 的使用做出自己的判断。以下是我个人的做法:

  • 如果图书馆网站的任何地方都有“框架”、“企业”、“反射”等字样,很可能与Proguard的兼容性很差;
  • 如果库与编译时代码生成有关(a-la Butterknife、Dagger 等),请在使用 Proguard 之前三思而后行;
  • 如果库与 JNI 混淆,请在使用 Proguard 之前多考虑几次,Google 会考虑它对 Proguard 的影响 即使你不缩小库本身
  • 如有疑问,请使用 Google 搜索和/或阅读库源代码:使用 Class.forNameProxy.getInvocationHandler 以及类似的反射代码通常是不好的迹象。

提供 Android UI 组件(例如 MPAndroidChart)的库通常可以缩小,至少如果您在 Gradle 配置中保留 getDefaultProguardFile('proguard-android.txt')

最重要的部分

很多开发者(包括 Proguard 开发者自己!)会给你一个误导性的建议,从空的 Proguard 配置 + 默认的 Android Proguard 配置开始,最终在必要时添加-keep 规则。

不要那样做!!

这些建议来自一些人,他们要么太坏而无法理解普通开发人员的问题(阅读:“Proguard 开发人员本人”),要么不知道如何正确使用 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 合作时遵循它!非常感谢!
  • 我有一个稍微不同的配置,我想和你的比较一下。我会把它作为一个单独的答案发布,因为将代码放在 SO 上的 cmets 中真的很难看。
  • 完全不同,但我们有相同的目标:避免维护地狱,不要使用混淆或任何会使应用程序变脆的东西......
【解决方案2】:

作为 ProGuard 的替代方案,您可以通过关闭 ProGuard 来使用 built-in Gradle shrinker,但仍引用 ProGuard 配置。这将删除未使用的代码,但不会混淆或执行任何其他“魔术”。虽然仅推荐用于 Debug 构建,但如果您认为不需要混淆,我不明白为什么不能将它用于 Release 构建。

与 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.** { *; }

【讨论】:

  • Erm... 这个配置根本不使用 Proguard。 useProguard false 完全禁用 Proguard,因此 proguardFiles 的值及其对应的内容没有区别。我不明白,这种配置如何使应用程序不那么脆弱(除非“使不那么脆弱”你的意思是不使用 Proguard 开始)。另外你为什么使用-dontwarn javax.**?这看起来真的很随意。至少让它-dontwarn javax.annotations.** 或类似的东西。
  • 看起来可能是这样,但请尝试一下。 proguard 文件被使用...由收缩器使用。
  • 关于 javax 的警告:shrinker 抱怨它们丢失了。由于它们是设备平台的一部分,我想可以抑制它
  • 有趣。我完全错过了为快速运行而引入的附加收缩器。确实,Proguard 有点慢,而且往往会做一些意想不到的事情(比如删除调试数据),所以新的收缩器可能会有一些用处。我建议您澄清一下,它与 Proguard 不同(并且可能链接到文章的specific section)。
【解决方案3】:

如果您通过 ProGuard 启用缩小,它也会缩小您的依赖项。

库通常尚未使用 ProGuard 进行混淆/缩小。如果某些库被混淆,默认情况下它们将无法正常工作,因此您应该检查您使用的任何库,看看它们是否有任何与 ProGuard 相关的文档。例如,Butterknife 有一些特殊的 ProGuard 规则,您需要包含这些规则以确保它继续正常工作。

【讨论】:

    【解决方案4】:

    对我来说,你应该寻找 multidex,超过 65k 的限制,而不是 proguard,因为在更长的时间里,后者不能解决你的问题。

    请参阅文档:https://developer.android.com/tools/building/multidex.html

    【讨论】:

    • 嗯,你为什么这么想?我认为它更优雅/专业(w.e.)没有那个膨胀的apk,不是吗?
    • 是的,拥有更小的 APK 总是更好(而且你可能会在投入生产之前运行 proguard + minify)但是即使在缩小之后你仍然可能超过 65k 方法。启用 multidex 是解决 65k 方法限制的“正确”方法。
    • 所以你们的意思是无论如何都需要启用multidex才能在“安全”方面?由于它易于使用,我只是实现了它。但我仍然想缩小我的代码;)
    • 是的,启用 multidex 可以解决您的问题directly。使用 proguard 可能(仍然是暂时的并且没有任何保证)可以解决问题,但这将是副作用。但正确的解决方案是multidex
    【解决方案5】:

    如果您在 build.grade 文件中启用缩小,那么是的,它也会缩小您的依赖项。

    请记住,Proguard 可能会引入不必要的副作用。并非所有库/依赖项都可以缩小,因为 Proguard 也会混淆代码。 (即将字符串名称转换为字符串 n)并删除未使用的代码。

    看看这个 Github 项目:https://github.com/krschultz/android-proguard-snippets

    作为替代方案,您可以考虑使用 MultiDex。你可以在这里阅读:https://developer.android.com/tools/building/multidex.html

    【讨论】:

      【解决方案6】:

      根据版本 3.2 中的新 Android Studio 更新,新的代码收缩器也通过将以下行添加到项目的 gradle.properties 文件中来进行混淆

      添加这一行:

      android.enableR8 = true
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-08
        • 1970-01-01
        • 2019-02-24
        • 2014-04-14
        • 1970-01-01
        • 2013-02-22
        • 1970-01-01
        • 2015-02-25
        相关资源
        最近更新 更多