I am moving a project from Ant to Gradle, but there's something I just can't figure out.
FACTS
After building a release APK (i.e., obfuscated), I noticed that the app was crashing badly. The error can be summed up by this:
java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
A debug (i.e., non obfuscated) APK works just fine, so I guessed it had to do with my ProGuard/DexGuard configuration.
I tried to keep the class reference by adding the following statement:
-keep class com.mypackage.MyCustomView
and, as a result, the release APK works just fine. I then did some research and I tried this more specific ProGuard/DexGuard configuration:
-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);
public void set*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
which also works, and it is class-independent.
QUESTION
I wonder:
- Why I don't have to deal with is while using Ant?
- What is the exact reason for that error to show up? (follows answer to first question)
ANSWER
The answer from @Blundell was substantially correct. Turns out I was missing one line from the build.gradle
configuration:
android {
...
buildTypes {
debug {
...
}
release {
proguardFile getDefaultDexGuardFile('dexguard-release.pro') # <----- this line
proguardFile 'dexguard-project.txt'
}
}
}
It appears that line is actually mandatory, since it serves as a base set of rules for ProGuard/DexGuard. In fact, this is part of the dexguard-release.pro
file:
-keepclassmembers !abstract class !com.google.ads.** 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);
public void set*(...);
}
-keepclassmembers !abstract class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
}
I found the documentation a little bit too vague on this, I hope it can be redacted to clear any ambiguity it might have. All in all, my fault.
LayoutInflater
is using them, but this cannot be deduced from static analysis of the source alone. I guess your real question is why is the ProGuard/DexGuard default config that used to be part of the SDK not used? My guess is this is because of recent changes in the Gradle plugin, along withrunProguard
being renamed and other such stuff. If you're using ProGuard from the SDK, check with the adt-dev Google group. If you're using DexGuard, check the download portal, there have been a few recent updates in quick succession. – Winifredwinikkawhy is the ProGuard/DexGuard default config that used to be part of the SDK not used
should mean. I am wondering why there is a different behavior in Ant and Gradle, since both are using the same version of DexGuard and both are using the same configuration file. – Krasnoff$ANDROID_HOME/tools/proguard
and it is or used to be the case thataapt
generates some additional config on the fly. One or the other may not be there, but I don't know this for a fact, I'm just guessing. It could be something stupid like the space in "program files" if you're on Windows. – Winifredwinikka