Is there an Enterprise+Proguard for dummies?

We’ve never used Proguard until now, because the vast majority of our code is in Lua and therefore obfuscated when it’s turned into bytecode by Corona’s build process.  

However we’re constantly running into Android’s 65k method count limit because we have integrated quite a number of libraries, so I’m now looking at Proguard as a way of (hopefully) stripping out any unused methods.

The problem is, I’m not really sure what I’m doing. I’ve enabled proguard in my project, and initially had loads of warnings such as

Warning: com.naef.jnlua.script.LuaScriptEngine: can't find referenced field 'javax.script.ScriptContext context' in class com.naef.jnlua.script.LuaScriptEngine

and notes such as

Note: com.ansca.corona.graphics.opengl.GLSurfaceView$GLThreadManager: can't find dynamically referenced class android.os.SystemProperties

Looking these kind of errors up online seemed to suggest that the solution was to use -dontwarn and -dontnote, but to me that seems like burying my head in the sand and pretending that the problems aren’t there.

I’m sure that if I go back and look at the integration docs for the various libs then I’ll find the proguard settings for those, but it seems that Corona’s own java code itself is encountering proguard problems.

I’ve added this to my proguard-project.txt:

-keep class com.quiztix.\*\* -keep class com.ansca.\*\* 

The project builds ok, but when I run my apk I get the following:

02-10 14:28:40.183: W/dalvikvm(8411): Exception Ljava/lang/NoSuchFieldError; thrown while initializing Lcom/ansca/corona/JavaToNativeShim; 02-10 14:28:40.183: W/dalvikvm(8411): Exception Ljava/lang/NoSuchFieldError; thrown while initializing Lcom/ansca/corona/CoronaEnvironment; 02-10 14:28:40.183: D/AndroidRuntime(8411): Shutting down VM 02-10 14:28:40.183: W/dalvikvm(8411): threadid=1: thread exiting with uncaught exception (group=0x418e6da0) 02-10 14:28:40.183: E/AndroidRuntime(8411): FATAL EXCEPTION: main 02-10 14:28:40.183: E/AndroidRuntime(8411): Process: com.quiztix.movies, PID: 8411 02-10 14:28:40.183: E/AndroidRuntime(8411): java.lang.NoSuchFieldError: no field with name='luaState' signature='J' in class Lcom/naef/jnlua/LuaState; 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.Runtime.nativeLoad(Native Method) 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.Runtime.doLoad(Runtime.java:435) 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.Runtime.loadLibrary(Runtime.java:363) 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.System.loadLibrary(System.java:526) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.ansca.corona.JavaToNativeShim.\<clinit\>(Unknown Source) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.ansca.corona.CoronaEnvironment.a(Unknown Source) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.ansca.corona.CoronaEnvironment.\<clinit\>(Unknown Source) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.quiztix.movies.CoronaApplication.onCreate(Unknown Source) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1025) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4581) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.app.ActivityThread.access$1600(ActivityThread.java:161) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1325) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.os.Handler.dispatchMessage(Handler.java:102) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.os.Looper.loop(Looper.java:157) 02-10 14:28:40.183: E/AndroidRuntime(8411): at android.app.ActivityThread.main(ActivityThread.java:5356) 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.reflect.Method.invokeNative(Native Method) 02-10 14:28:40.183: E/AndroidRuntime(8411): at java.lang.reflect.Method.invoke(Method.java:515) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265) 02-10 14:28:40.183: E/AndroidRuntime(8411): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081) 02-10 14:28:40.183: E/AndroidRuntime(8411): at dalvik.system.NativeStart.main(Native Method)

I can’t see any examples of proguard being used in a Corona Enterprise project, and the few other Corona devs I know have also not used it.  

Does anyone know how I can get it up and running?

Update:

I made a slight change to the file, so that keep was formatted like so:

-keep class com.quiztix.\*\* { \*; } -keep class com.ansca.\*\* { \*; } -keep class com.naef.\*\* { \*; }

This gets rid of the errors regarding those files, but then moves onto the next jar, and the next once I fix that, and so on.

It’s my understanding that proguard is supposed to work out what classes/functions/etc should stay in the app, and then remove the others. But it seems to be that it’s stripping everything out, and my only solution is to say “keep this class” for every class, which defeats the point of using proguard to remove unneeded parts of libraries. 

Again, if anyone can point me in the right direction, it would be greatly appreciated.

ProGuard’s primary use case is to obfuscate compiled Java code.  And you would normally only apply it to your own code.  Never to 3rd party code and libraries that doesn’t belong to you.  The reason for this is because ProGuard will break code which depends on Java reflection since ProGuard will rename/mangle Java members.  For example, it would definitely break Corona’s plugin system since they’re loaded by the name submitted to the Lua require() function.

While ProGuard can strip out Java members it thinks will never be called, that’s not its main intended usage.  That’s my point.  And again, it would break Corona’s plugin system (and perhaps things on Google’s end too) since ProGuard will wrongfully strip out classes/members that are invoked via reflection.  It shouldn’t be used to reduce the public member count on Android.  That’s what Google’s multidexing feature is for.

Thanks for the reply Joshua.  

Since Proguard won’t work for our needs, how would you suggest we address the method count limit? It’s an increasingly big problem for us as we’re always skirting right on the limit, in the past we’ve been fortunate that there were old libs we no longer needed which we could remove but that’s no longer the case.  

Many SDKs recommend using Gradle, and that can be used to selectively compile APIs:

https://developers.google.com/android/guides/setup#split

The GPGS SDK is the largest one in our project (around 15k methods if I remember correctly) but we hardly use any of the features so it would be good if we could remove the unnecessary ones (things like Google Panorama Viewer and Google Drive are not relevant in our project). On the page I linked I can only see a way to do this using Gradle (in fact most SDKs I come across now have instructions for Gradle as the primary build method), is Gradle support anywhere on the Corona horizon?

Hi
It Looks like a possible answer for you came out in another thread…
https://forums.coronalabs.com/topic/54273-android-studio-integration-status/#entry319246

We don’t have official Android Studio support for Corona Enterprise yet.  We plan on doing so later this year.  However, you can convert the project yourself as the person above has linked to.  And if you do switch over to Android Studio and gradle, you’ll be able to ProGuard the Google Play Services and Google Play Game Services according to Google’s documentation which will remove APIs much more intelligently.

Update:

I made a slight change to the file, so that keep was formatted like so:

-keep class com.quiztix.\*\* { \*; } -keep class com.ansca.\*\* { \*; } -keep class com.naef.\*\* { \*; }

This gets rid of the errors regarding those files, but then moves onto the next jar, and the next once I fix that, and so on.

It’s my understanding that proguard is supposed to work out what classes/functions/etc should stay in the app, and then remove the others. But it seems to be that it’s stripping everything out, and my only solution is to say “keep this class” for every class, which defeats the point of using proguard to remove unneeded parts of libraries. 

Again, if anyone can point me in the right direction, it would be greatly appreciated.

ProGuard’s primary use case is to obfuscate compiled Java code.  And you would normally only apply it to your own code.  Never to 3rd party code and libraries that doesn’t belong to you.  The reason for this is because ProGuard will break code which depends on Java reflection since ProGuard will rename/mangle Java members.  For example, it would definitely break Corona’s plugin system since they’re loaded by the name submitted to the Lua require() function.

While ProGuard can strip out Java members it thinks will never be called, that’s not its main intended usage.  That’s my point.  And again, it would break Corona’s plugin system (and perhaps things on Google’s end too) since ProGuard will wrongfully strip out classes/members that are invoked via reflection.  It shouldn’t be used to reduce the public member count on Android.  That’s what Google’s multidexing feature is for.

Thanks for the reply Joshua.  

Since Proguard won’t work for our needs, how would you suggest we address the method count limit? It’s an increasingly big problem for us as we’re always skirting right on the limit, in the past we’ve been fortunate that there were old libs we no longer needed which we could remove but that’s no longer the case.  

Many SDKs recommend using Gradle, and that can be used to selectively compile APIs:

https://developers.google.com/android/guides/setup#split

The GPGS SDK is the largest one in our project (around 15k methods if I remember correctly) but we hardly use any of the features so it would be good if we could remove the unnecessary ones (things like Google Panorama Viewer and Google Drive are not relevant in our project). On the page I linked I can only see a way to do this using Gradle (in fact most SDKs I come across now have instructions for Gradle as the primary build method), is Gradle support anywhere on the Corona horizon?

Hi
It Looks like a possible answer for you came out in another thread…
https://forums.coronalabs.com/topic/54273-android-studio-integration-status/#entry319246

We don’t have official Android Studio support for Corona Enterprise yet.  We plan on doing so later this year.  However, you can convert the project yourself as the person above has linked to.  And if you do switch over to Android Studio and gradle, you’ll be able to ProGuard the Google Play Services and Google Play Game Services according to Google’s documentation which will remove APIs much more intelligently.