Switching to Gradle: why do I have to keep custom views from being obfuscated?
Asked Answered
K

1

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.

Krasnoff answered 14/12, 2014 at 15:20 Comment(7)
how/where do you create your custom View?Breakfront
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.Winifredwinikka
@Winifredwinikka 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.Krasnoff
@Breakfront Just standard custom views. I've defined all the possible constructors, which are only calling super().Krasnoff
@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.Winifredwinikka
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 :) )Krasnoff
Can you show your Proguard cofiguration in build.gradle?Nardone
C
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).

Chromatology answered 15/12, 2014 at 11:58 Comment(2)
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).Krasnoff
I updated the OP with your answer and the reason for this behavior.Krasnoff

© 2022 - 2025 — McMap. All rights reserved.