We hit this with Mustbin's app a third of the way into development and I had to solve this issue so we could continue. I ended up trying the Proguard approach first because the custom class loader approach seemed a bit like doing binary overlays from ages past and using jarjar to reduce the size of external libraries meant we couldn't just include the libraries through Maven Central.
The Proguard approach is relatively straightforward and lets you debug your application normally, though it does increase your compile time which is bad enough with Android Studio and Gradle (they don't background compile like Eclipse did so it takes nearly a minute for each compile cycle). You don't know if you're took out a method by mistake until runtime when you'll get an error that method is not found. When you see the error in Logcat, you have to add a -keep rule so that it's not removed by Proguard; you can use the app\build\outputs\proguard\<flavor>dump.txt output to help you with the syntax to the keep rule needed to add it back. You also have to enable this in your build.gradle file for your debug builds.
Here is the proguard-rules.txt file that Mustbin uses minus Mustbin-specific keep rules. Mustbin uses Crashlytics, AWS, GreenRobot, Robospice, Retrofit, Butterknife, Spongy Castle, and the metadata-extractor EXIF library so the rules for these are show below (if you have rules for other libraries, feel free to reply to this and add them so others can use them):
#this disables obfuscation so we're just using this to trim unused methods/classes
-dontobfuscate
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
-dontoptimize
# Most of these options are from
# http://www.crashlytics.com/blog/mastering-proguard-for-building-lightweight-android-code/
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-libraryjars libs
# public Android APISs
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.content.Context
-keep public class * extends android.support.v4.content.LocalBroadcastManager
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.billing.InAppBillingService
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.MenuItem);
}
# Crashlytics
-keep class com.crashlytics.** { *; }
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
# Butterknife
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
# Amazon AWS SDK
-keep class org.apache.commons.logging.** { *; }
-keep class com.amazonaws.services.sqs.QueueUrlHandler { *; }
-keep class com.amazonaws.javax.xml.transform.sax.* { public *; }
-keep class com.amazonaws.javax.xml.stream.** { *; }
-keep class com.amazonaws.services.**.model.*Exception* { *; }
-keep class org.codehaus.** { *; }
-keepattributes Signature,*Annotation*
-dontwarn javax.xml.stream.events.**
-dontwarn org.codehaus.jackson.**
-dontwarn org.apache.commons.logging.impl.**
-dontwarn org.apache.http.conn.scheme.**
-keep class com.amazonaws.internal.config.** { *; }
# Jackson library
-keepattributes *Annotation*,EnclosingMethod
-keep public class mydatapackage.** {
public void set*(***);
public *** get*();
}
-keepattributes Signature
-keepnames class com.fasterxml.jackson.** { *; }
-dontwarn com.fasterxml.jackson.databind.**
# GreenRobot's event methods
-keepclassmembers class ** {
public void onEvent*(**);
}
# SpongyCastle
-keep class org.spongycastle.**
# Robospice
# For RoboSpice
#Results classes that only extend a generic should be preserved as they will be pruned by Proguard
#as they are "empty", others are kept
-keep class com.mustbin.mustbin.api.**
#RoboSpice requests should be preserved in most cases
-keepclassmembers class com.mustbin.mustbin.api.** {
public void set*(***);
public *** get*();
public *** is*();
}
### Jackson SERIALIZER SETTINGS
-keepclassmembers,allowobfuscation class * {
@org.codehaus.jackson.annotate.* <fields>;
@org.codehaus.jackson.annotate.* <init>(...);
}
## Gson SERIALIZER SETTINGS
# See https://code.google.com/p/google-gson/source/browse/trunk/examples/android-proguard-example/proguard.cfg
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# Gson specific classes
#-keep class sun.misc.Unsafe { *; }
#Retrofit
-keep class com.google.gson.** { *; }
-keep class com.google.inject.** { *; }
-keep class org.apache.http.** { *; }
-keep class org.apache.james.mime4j.** { *; }
-keep class javax.inject.** { *; }
-keep class retrofit.** { *; }
#EXIF library
-keep class com.adobe.xmp.** { *; }
-keep class com.adobe.xmp.XMPMetaFactory { *; }
-keep class com.drew.** { *; }
# supporess gazillions of warnings last
-dontwarn org.apache.**
-dontwarn com.google.**
-dontwarn javax.**
-dontwarn com.adobe.**
-dontwarn org.ietf.**
-dontwarn com.amazonaws.**
-dontwarn rx.**
-dontwarn com.squareup.**
-dontwarn org.w3c.**
-dontwarn org.codehause.**
-dontwarn retrofit.**
-dontwarn java.beans.**
1. ken11/26/2014 16:40:07
Optimizations are turned off so you can still debug the app w/o it jumping randomly when you step through code because Proguard decided to rewrite your code to be more "optimal" (and to obfuscate it)...it's pretty standard practice..
2. Jon11/20/2014 22:26:39
I notice that most people who recommend ProGuard as a tool to help with the 64k method limit use -dontoptimize and -dontobfuscate.
The -dontoptimize seems counterintuitive for this purpose, but everyone seems to use it.
Can you shed any light on what this means relative to the method limit problem and why we're turning off optimizations for that?
Su | Mo | Tu | We | Th | Fr | Sa |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |