如何使用Proguard缩小Android代码

peb*_*les 0 android dex-limit android-proguard

由于我在我的应用程序中使用了很多依赖项,因此我达到了65k方法限制(我达到了76k方法).我在android.developer上读过,proguard用于缩小代码.

那么 - proguard只会缩小我的应用程序代码还是缩小我的依赖项代码呢?使用proguard缩小代码时,是否需要警惕某些事情?我怎么做?

我的Gradle Build:

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'
}
Run Code Online (Sandbox Code Playgroud)

use*_*723 13

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.** { *; }
Run Code Online (Sandbox Code Playgroud)

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

评估Proguard的依赖关系

如上所示,即使是经验丰富的开发人员有时也会选择忽略Proguard.如果谷歌搜索图书馆并且它与Proguard的兼容性没有任何返回(即使它们确实返回了一些结果!),您可能必须自己判断Proguard的使用情况.这是我个人的做法:

  • 如果图书馆网站上的任何地方都有"框架","企业","反思"等词语,那么它很可能与Proguard不兼容;
  • 如果库与编译时代码生成有关(a-la Butterknife,Dagger等),请在使用Proguard之前三思而后行;
  • 如果图书馆与JNI混淆,那么在使用Proguard之前要多考虑几次,而Google 也要考虑它对Proguard的影响,即使你不缩小图书馆本身 ;
  • 如果有疑问,谷歌为它和/或阅读库源代码:使用Class.forName以及Proxy.getInvocationHandler类似的反射代码是通常的坏迹象.

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

最重要的部分

许多开发人员(包括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.**,** { *; }
Run Code Online (Sandbox Code Playgroud)

将上述规则添加到您的应用程序中proguard-rules.pro,它们将仅缩小您明确允许缩小的类.将其他安全可收缩包装的通配符(与上面的!.**部分完全相同)添加到-keep行首.