There’s no question that Proguard is an indispensable tool when it comes to Java development (Android development in particular) but because Proguard’s configuration is completely text based, there’s a lot of room for human error. To make matters worse, the compiler can’t tell us if we got something wrong. Instead we’re left to discover the effects at runtime.
This is where marker interfaces come in. Just in case you aren’t familiar, a marker interface is an empty interface used to provide hints about how a given class should be handled in a given context. You could, for example, use a marker interface to signal that a certain class should be serialized using a specific method.
Marker interfaces can also be leveraged to make Proguard a little more OO friendly. Let’s say we have a package com.foo.model. Inside this package are a number of models used for REST communication via JSON and we’d like to reuse the Java class’ field names in our JSON objects. Assume that all of our models extend from FooModel.
Normally, we’d either add a rule not to obfuscate anything in the com.foo.model package, or not to obfuscate anything that extends com.foo.model.FooModel. But maybe next week we need to implement com.bar.model.BarModel. While it’s not hard to copy/paste a rule for this scenario, as mentioned above, there’s no compile time check to tell us whether we fat fingered the package/class names, and it just adds clutter to what’s typically an already cluttered config file.
A better solution is to create a marker interface:
package com.foo.util; /** * Empty interface to allow quick disabling of all obfuscation of implementors. */ public interface Unobfuscable {}
And add a rule into Proguard for that interface:
-keep interface com.foo.util.Unobfuscable -keep class * implements com.foo.util.Unobfuscable { *; }
Note the first rule that protects the interface it’s self. This is necessary because Proguard will otherwise remove the interface in it’s shrink phase, effectively disabling the second rule.
With these pieces in place, we can reuse our Proguard rule anywhere we need simply by implementing the Unobfuscable interface. We can apply it to a base class such as FooModel, causing extending models to inherit FooModel’s obfuscation rules, or we can apply it to only a few specific classes. Using this approach we can create additional interfaces that map to different obfuscation rules as needed.
It’s now also clearer to other developers that special behavior is present and we be confident our rule contains no typos as we apply it to new classes.