Since I am using many dependencies in my app, I am reaching the 65k Method Limit (I am reaching 76k methods). I've read on android.developer that proguard is used to shrink the code.
So - does proguard only shrink my application code or does it shrink the code of my dependencies too? Do I need to be wary of something when shrinking code with proguard? How do I do that?
My 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'
}
TL; DR: invert your
-keep
option unless you love troublesFirstly: I believe, that you are making right choice by using Proguard to overcome the dex limitation. I would not recommend using multidex support library under any circumstances: it introduces problem of multiple classloaders in your application, and that may backfire in many non-obvious ways.
Here is my personal approach to shrinking the app efficiently:
Picking dependencies for shrinking
In your case, there aren't many (direct) dependencies in the first place. You may want to look at output of
gradlew dependencies
to get better idea of your indirect dependencies, some of which may be biggest contributors to total app size. Then you may proceed to use some of tools listed in "Dex" section of Android Arsenal to learn which libraries contribute most to dex method count. You seem to already have a general idea of it, so I won't dwell much on this part.Remember: shrinking executable code is somewhat non-trivial intervention in library internals, so you'd rather shrink less to avoid mysterious problems in future. If in doubt, start from libraries, that openly declare, that they do support Proguard officially (in your case that would be Android Support libraries).
Note, that "supporting Proguard" may mean different things for different developers. You can expect Android Support Library developers to be at least basically competent, but many others will ship with consumer Proguard rules like this:
In case you wonder, the above config is based upon many real-life configs, such as Square's Leak Canary Proguard configuration. It does not say anything about overall competency of developers in question, just reminder that using Proguard can be hard. And yes, this kind of configuration will completely prevent shrinking and obfuscation of the library, unless you build it's local copy from source code and remove such helpful
consumer-proguard-rules.pro
from there.Evaluating dependencies for Proguard
As shown above, even experienced developers sometimes choose to ignore Proguard. If Google searches regarding the library and it's compatibility with Proguard return nothing (and even if they do return some results!) you may have to make your own judgement regarding usage of Proguard. Here is how I personally do:
Class.forName
as well asProxy.getInvocationHandler
and similar reflection code are usual bad signs.Libraries, that offer Android UI components (such as MPAndroidChart) are usually ok to shrink, at least if you keep
getDefaultProguardFile('proguard-android.txt')
in your Gradle config.The most important part
A lot of developers (including Proguard developers themselves!) will offer you a misguided recommendation to start from empty Proguard config + default Android Proguard configuration, and eventually add
-keep
rules when necessary.DO NOT DO THAT!!
Those advices come from people, who are either too badass to understand problem of average developer (read: "the Proguard developer himself") or don't have a clue about using Proguard properly. In fact, these kind of misguided practices are the very reason, why many answers to this question warn you against using Proguard: it's default behavior is like suggesting someone to start mountaineering from scaling the Everest.
Default Proguard configuration will obfuscate, shrink and optimize everything—your entire application with all dependencies except some classes you explicitly exclude. You don't want that, unless you have absolute understanding of every library and line of code in your projects: how they work and interact with each other, which techniques they internally use etc.
Instead you want to do the minimal necessary intervention (shrinking the code to reduce dex method count) in the minimal possible range (few hugest libraries) with minimal consequences (only where Proguard is known to work for sure). Here is my Proguard config for such cases:
Add the above rules to your app's
proguard-rules.pro
, they will shrink only classes, that you explicitly allow to shrink. Append wildcards for other safely shrinkable packages (exactly as above—with!
and.**
parts) to beginning of the-keep
line.