Question

I am working on finishing up a project, and it's my first time using Proguard. I have a method to set background colors for the activity itself as well as some buttons. Before Proguard everything works fine. After Proguard the colors aren't set.

Before:

 public void setBackgroundColor(String color, View background){

    String id = "1";       
    try {
        ColorId myObject = new ColorId();
        Method method = ColorId.class.getMethod(color);
        id = (String) method.invoke(myObject);

    } catch (Exception e) {
        e.printStackTrace();
        id = "1";
    }

    int thisColor = Integer.valueOf(id);

    switch(thisColor) {

    case 0://black
        background.setBackgroundColor(0xff000000);

        break;


    case 1://white
        background.setBackgroundColor(0xffffffff);

        break;

    case 2://red
        background.setBackgroundColor(0xffCC0000);

        break;

    ...

    default:

        background.setBackgroundColor(0xff0099cc);
        break;
    }

}

After:

public void a(String paramString, View paramView){
    try {
       c localc = new c();
        str = (String)c.class.getMethod(paramString, new Class[0]).invoke(localc, new Object[0]);
        switch (Integer.valueOf(str).intValue()){
           default: 
             paramView.setBackgroundColor(-16737844);
             return;
         }
      }catch (Exception localException){
          for (;;){
             localException.printStackTrace();
             String str = "1";
      }
      paramView.setBackgroundColor(-16777216);
      return;
    }
  paramView.setBackgroundColor(-1);
  return;
  paramView.setBackgroundColor(-3407872);
  return;
  paramView.setBackgroundColor(-16737844);
  return;
  paramView.setBackgroundColor(-8355712);
  return;
  paramView.setBackgroundColor(-6697984);
  return;
  paramView.setBackgroundColor(-17613);
  return;
  paramView.setBackgroundColor(-5609780);
  return;
  paramView.setBackgroundColor(-35700);
}

Can anyone help explain what is happening here, and how I can make this method (and others in the future) work again after obfuscation? To me it looks like Proguard is rearranging things in regards to the switch.

Was it helpful?

Solution

Proguard shortens code by renaming classes and methods to have shorter names and by removing code that isn't referred to. Your code doesn't work because Proguard renamed or removed the ColorId methods black(), white(), and red(). To use reflection, you'd need to add Proguard keep directives to tell it to keep these methods and to keep their original names.

I don't have an explanation for why the "after" code's switch statement is messed up. Are you sure you decompiled it properly?

Why is the "before" code so convoluted? It uses reflection to look up a method by color name, then calls it to translate the color name to a String, parses the String to get an integer code, boxes the integer code into an Integer, unboxes it, uses a switch statement to pick a color value, then sets the background color, replicating the background.setBackgroundColor() call in each of the switch branches (breaking the DRY principle).

Reflection is an extreme tool to use in special cases like dynamically loaded code.

It'd be simpler, faster, and clearer to look up the color name in a HashMap:

static final int DEFAULT_COLOR = 0xff0099cc;
static final Map<String, Integer> colors = new HashMap<String, Integer>();
static {
  colors.put("black", 0xff000000);
  colors.put("white", 0xffffffff);
  colors.put("red",   0xffCC0000);
}

public void setBackgroundColor(String color, View view) {
  Integer colorInteger = colors.get(color);
  int colorValue = colorInteger == null ? DEFAULT_COLOR : colorInteger.intValue();
  view.setBackgroundColor(colorValue);
}

This HashMap is a good choice if the color has to be passed in as a string name. But if you can change the color argument, an enum would be more type safe, simpler, and faster:

public enum Color {
  BLACK(0xff000000), WHITE(0xffffffff), RED(0xffCC0000), DEFAULT(0xff0099cc);

  final int value;
  Color(int value) { this.value = value; }
}

public void setBackgroundColor(Color color, View view) {
  view.setBackgroundColor(color.value);
}

[It's better to define all your color values in an Android resource file (colors.xml). You can look them up by resource ID number.]

OTHER TIPS

What is your proguard setup? Post your proguard-project.txt file (or proguard.cfg if you're using the old method). I'd suggest turning off obfuscation to see more clearly how the code is being changed. Use '-dontobfuscate'.

The 'after' code looks odd. Are you using the optimization common configuration file (proguard-android-optimize.txt)? If so, try to use without optimization to reduce how much your code is being modified.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top