12

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:

  1. Why I don't have to deal with is while using Ant?
  2. 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.

Sebastiano
  • 12,289
  • 6
  • 47
  • 80
  • how/where do you create your custom View? – pskink Dec 14 '14 at 15:28
  • The constructors have to be kept because the `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 with `runProguard` 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. – Barend Dec 14 '14 at 15:56
  • @Barend I'm not sure what `why 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. – Sebastiano Dec 14 '14 at 16:00
  • @pskink Just standard custom views. I've defined all the possible constructors, which are only calling super(). – Sebastiano Dec 14 '14 at 16:01
  • @dextor there are some default configs that ship with the SDK in `$ANDROID_HOME/tools/proguard` and it is or used to be the case that `aapt` 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. – Barend Dec 14 '14 at 16:05
  • OK, thanks. I'll try to ask DexGuard support about this and post an answer as soon as I get one. (OSX, btw, so no spaces for me thanks :) ) – Sebastiano Dec 14 '14 at 16:09
  • Can you show your Proguard cofiguration in build.gradle? – Marcin Koziński Dec 15 '14 at 12:03

1 Answers1

4

Most likely Ant was using a different configuration file,

Also with Gradle you need to explicitly state you want to also use the Android proguard config file i.e. use multiple rules files like so:

    proguardFile getDefaultProguardFile('proguard-android.txt')
    proguardFile 'your/sepcific/folder/proguard.cfg'

(I remember Ant never used a SDK proguard file and it used to be recommended to copy all the config across).

Blundell
  • 75,855
  • 30
  • 208
  • 233
  • No, I am (still) using the same configuration file. The only difference now is the keeping of the custom views. Plus, I double checked my build.gradle, it is correctly pointing to my configuration file (otherwise, other stuff would stop working as well). – Sebastiano Dec 15 '14 at 12:01
  • I updated the OP with your answer and the reason for this behavior. – Sebastiano Dec 15 '14 at 16:31