Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proguard makes reflection with the R class in android application no longer work

I have activated proguard in my project since I am trying to release this on Google Play. Even though proguard-android.txt has

-keepclassmembers class **.R$* {
public static <fields>;

}

which means it should not obfuscate the R.raw class that I need, I find that when running this code

import headwayEnt.Blackhole_Darksun.R;

private static final String RES_PATH = "headwayEnt.Blackhole_Darksun.R";

public static int getFileHandle(String fileName, String path) {
    String fullPath = RES_PATH + '$' + path;
    if (subClass == null) {
        Class<headwayEnt.Blackhole_Darksun.R> c = null;     
        try {
            c = (Class<R>)Class.forName(RES_PATH);
        } catch(ClassNotFoundException e) {
            e.printStackTrace();
        } catch(ClassCastException e) {
            e.printStackTrace();
        }
        subClass = c.getDeclaredClasses();
    }
    int fileHandle = 0;
    try {
        int i = 0;
        System.out.println("fullPath: " + fullPath + " subclass len: " +
                subClass.length);
        for (; i < subClass.length; ++i) {
            String name = subClass[i].getName();
            if (subClass[i].getName().equals(fullPath)) {
                break;
            }
        }
        System.out.println("found i: " + i);
        Field[] f = subClass[i].getDeclaredFields();
        for (i = 0; i < f.length; ++i) {
            if (f[i].getName().equals(fileName)) {
                break;
            }
        }

        try {
            fileHandle = f[i].getInt(null);
        } catch(IllegalAccessException e) {

        }
    } catch (RuntimeException e) {
        System.out.println("Could not find filename: " + fileName + 
                " with path: " + path);
        throw e;
    }
    return fileHandle;
}

with path raw for example and a filename from there I get an ArrayIndexOutOfBoundsException at line

Field[] f = subClass[i].getDeclaredFields();

since subClass.length is 0.

This thing works just fine if not obfuscating so I figured that the problem is with proguard.

I tried playing with different ways to make it not obfuscate the R classes like this:

-keep public class headwayEnt.HotshotEngine.Resource.ENG_Resource { *; }
-keep class headwayEnt.HotshotEngine.** { *; }
-keep class **.R$*
-keep public class headwayEnt.Blackhole_Darksun.R { *; }
-keep public class headwayEnt.Blackhole_Darksun_Full.R { *; }
-repackageclasses ''
-keep public class **.R {
  public *;
}
-keep public class **.R$* {
  public *;
}

and still doesn't work.

I must mention that all of my code is in a library (headwayEnt.Blackhole_Darksun) and that I reference that library in headwayEnt.Blackhole_Darksun_Full. Basically I am building two versions from the same code, one for full version and one for the free demo. All this obfuscation happens in the application, not in the referenced library (just to be clear).

like image 257
Sebastian Bugiu Avatar asked Dec 17 '12 23:12

Sebastian Bugiu


People also ask

How do I enable R8 on my Android?

To enable R8, open build. gradle module app file and add this piece of code inside the buildTypes . The code inside the release{} block means that this will be applied to the release build version of your application. If you launch the app in the emulator, this code is not executed.

How do you keep a class in ProGuard?

-keepclassmembernames. This is the most permissive keep directive; it lets ProGuard do almost all of its work. Unused classes are removed, the remaining classes are renamed, unused members of those classes are removed, but then the remaining members keep their original names.

What is the use of ProGuard in Android?

ProGuard is a tool to help minify, obfuscate, and optimize your code. It is not only especially useful for reducing the overall size of your Android application as well as removing unused classes and methods that contribute towards the intrinsic 64k method limit of Android applications.


1 Answers

This should work:

-keepattributes InnerClasses

-keep class **.R
-keep class **.R$* {
    <fields>;
}

The InnerClasses attribute is necessary to get anything from Class$getDeclaredClasses(). The -keep options are necessary to keep the relevant classes and fields, with their original names.

like image 186
Eric Lafortune Avatar answered Nov 09 '22 23:11

Eric Lafortune