If you use Eclipse plugin for your Android app, you have a generated proguard.cfg file in your project, and it probably contains the following:
-optimizationpasses 5 -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -dontpreverify -verbose -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService -keepclasseswithmembernames class * { native <methods>; } -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet, int); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; }Not going to details, the file has everything one could possibly need. Except one thing.
If you are working with GUI (and most likely you are), you might have onClick methods in your layout XMLs. Something like this:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/mytext" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="This is TEXT" /> <Button android:id="@+id/mybutton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="The Button"Usual stuff. Everything works before obfuscation, but after ProGuard you will get:android:onClick="myOnClickHandler"
/> </LinearLayout>
java.lang.IllegalStateException: Could not find a method myOnClickHandler(View) in the activity class com.mypackage.MyActivity for onClick handler on view class android.widget.Button at android.view.View$1.onClick(View.java:2059) at android.view.View.performClick(View.java:2408) at android.view.View$PerformClick.run(View.java:8817) at android.os.Handler.handleCallback(Handler.java:587) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:143) at android.app.ActivityThread.main(ActivityThread.java:4914) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:521) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.NoSuchMethodException: onActionSelectLevel at java.lang.ClassCache.findMethodByName(ClassCache.java:308) at java.lang.Class.getMethod(Class.java:985) at android.view.View$1.onClick(View.java:2052) ... 11 moreOkay, so if there is no more
myOnClickHandler
method than probably ProGuard has renamed it. To check this out I've looked into proguard/mapping.txt where the compiler saves the obfuscation reports (in Eclipse). And I found no references to such a function at all! WTF?Well, the answer is simple: ProGuard not just performs renaming, it also optimises the code. And since
myOnClickHandler
is not referenced from Java code it gets thrown away. Totally.So after some RTFM-activity I got these lines, that I think should be inserted into the proguard.cfg by default:
-keepclassmembers class * { public void *(android.view.View); } -keepclassmembers class **.R$* extends android.app.Activity { public static <fields>; }The first part keeps potential onClick handlers. It might be optimised to
class * extends android.app.Activity
, it's just my precautions. The second part is actually taken from ProGuard official website. It preserves R
class from being optimised or renamed. I assume, this is also a must have.And one more thing. Make sure that you always increase the version number in AndroidManifest.xml. First time when I did the obfuscation using the standard config, my app worked while it shouldn't have. Clever Android did not updated the app with the obfuscated package because the version number was the same as in unobfuscated one.
Hope you find this useful.
No comments:
Post a Comment