BEEPS AND BLIPS FROM OUTERSPACE
USING PROGUARD FOR ANDROID AND LIBGDX

I decided to finally bite the bullet and get ProGuard working for some Android apps.  It was hell getting everything working because of the shear number of moving parts: Android SDK, Eclipse, Libgdx, AdMob/Google Ads, apk signing, and the shitstorm ProGuard imposes on any project.

This is going to be another Impatient Guide, because I'd rather not relive configuration problems.  The amount of time wasted getting technologies configured and working together is a time sink that gets in the way of making real progress and doing real work.

Step one:  Turn on ProGuard for your Android project.

  • Navigate to your project.  If you are using LibGdx and the standard configuration it will be named something like "<YourProjectName>-Android".
  • Open default.properties for editing
  • Add the line: proguard.config=proguard.cfg
  • Save and reopen Eclipse.

Step two:  Update the Android SDK Location in Eclipse

Did you jump the gun and try to created a signed apk?  Did you get this error?

"C:\Program' is not recognized as an internal or external command, and executable program, or command file."

Well, Proguard doesn't like spaces in paths, so update the Android SDK location to a short path form.

  • Open a command prompt: WIN+R -> cmd.exe
  • cd to the Android SDK location... e.g. "c:\Program Files (x86)\Android\android-sdk"
  • type: for /d %I in (*) do @echo %~sI
  • Copy the path.  e.g. "C:\PROGRA~2\Android\ANDROI~1"
  • Paste the short form path in Eclipse: Window -> Preferences -> Android -> SDK Location
  • Hit apply or OK

Step Three: Tell ProGuard about dependent libraries

Did you try to export an application package again?  Did you get errors like this?

"Warning: com.badlogic.gdx.scenes.scene2d.ui.utils.DesktopClipboard: can't find superclass or interface java.awt.datatransfer.ClipboardOwner"

or

"Warning: there were 28 unresolved references to classes or interfaces."

Well, you have to tell ProGuard dependent jar files, so it won't blow up.  Add something like the following to the proguard.cfg file in your project directory.  Your configuration will depend upon how your project is set up and make sure to use absolute paths.

-libraryjars 'C:\Program Files\Java\jre6\lib\rt.jar'
-libraryjars 'C:\source...\<YourProjectName>-Android\libs'
-libraryjars 'C:\source...\<YourProjectName>\libs'
-libraryjars 'C:\source...\<YourProjectName>\libs\GoogleAdMobAdsSdkAndroid-4.1.1'

That last one is only valid if you use the AdMob SDK.

Step Four: Fix runtime errors resulting from an overzealous ProGuard shrink

Did you export an .apk with joy, but become totally crushed when your application crashed?  Did the crashes happen when you clicked on widgets?

It turns out that ProGuard is removing any code that appears to not be referenced/used.  So, any OnClick* handlers specified in your Android layout xml files will be removed.  Seriously.  Take a look at <YourProjectName>-Android/proguard/usage.txt.  That is stuff that was torn out from your code to "speed" up performance.  Well, it crashes faster.  That's for sure.

But you can't blame ProGuard.  It can't read every file in your project that is SDK dependent and make the connection.  ProGuard is nice, but it's no mindreader.

To fix this, open up proguard.cfg again and tell ProGuard to not touch any OnClick handlers:

-keepclasseswithmembers class * {
  void onClick*(...);
}

If you want to make sure your advertisements from Google show up (com.google.ads*), add the following to your config file too:

-keep public class com.google.ads.** {*;}

Save it.

Step Five: Export the .apk and test it

You've probably already done this, but for reference:

  • Right Click on <YourProjectName>-Android -> Android Tools -> Export Signed Application Package...
  • Complete the form and generate the apk (e.g. yourproject.apk)
  • Make sure the app isn't already installed on your phone
  • Open a console: apk.exe install yourproject.apk

Conclusion:

This article was built upon a previous article called How to Obfuscate and Package a LibGdx App for Distribution.

Working with ProGuard is painful, but hopefully your project is now up and running.

 

POSTED 2012-08-22 17:32:24 CATEGORY ENGINEERING TAGS GOOGLE ADS LIBGDX PROGUARD HELL ANDROID IMPATIENT GUIDE
HOW TO OBFUSCATE AND PACKAGE A LIBGDX APP FOR DISTRIBUTION

First off: This is an Impatient Guide and it will be short on explanation and possibly accuracy

Second off: This method is a first attempt and is probably fraught with errors and inefficiencies, but it worked for me.

Let's get started!

Say you have a LibGDX project and you want to package it up for redistribution, but you don't want to give out a jar and would like it to be wrapped in a nice executable (.exe) just like any ordinary Windows binary.  In addition, obfuscating the code to make reverse engineering more difficult might be something you want too.

Here's how I did it manually with Eclipse, ProGuard, Launch4j, and 7-zip.

Creating the JAR files:

  1. Export a jar file with the Eclipse Export Wizard.  I did this for my desktop version, by right clicking on the project "myapp-desktop" -> Export -> Java -> Runnable Jar File.
  2. Plug in the usual stuff into the wizard, but choose "Copy required libraries into a sub-folder next to the generated JAR."  Otherwise, ProGuard will make your life a nightmare when you try to obfuscate (e.g. "Warning: org.lwjgl.input.Controllers: can't find referenced class net.java.games.input.ControllerEnvironment").
  3. Hit "finish" and the wizard will generate your shiny new .jar file.
  4. Do 1 through 3 again, but in step 2 choose "Package required libraries into generated JAR."  The jarinjarloader files and the MANIFEST.MF from this second generated JAR will be used later on.

Obfuscate your jar with ProGuard:

  1. Run the GUI version of ProGuardjava -jar proguardgui.jar
  2. In the Input/Output tab, click "Add input..." and enter the first generated JAR path.
  3. Click "Add output..." and enter your output JAR... say (e.g. output.jar).
  4. Click "Add..." in the Add libraries section and add the library export directory generated with the first JAR.  If your export JAR name was foo.jar, the export dependencies should be in foo_lib.
  5. In the Obfuscation tab uncheck "Use mixed-case class names."
  6. Keep the default settings for now (you can tweak them later once you have this working).
  7. Start processing the JAR.  In the Process tab click Process!
  8. If ProGuard finishes it will produce a nice obfuscated/shrunk JAR, output.jar.

Combine library dependencies into obfuscated JAR

  1. The output.jar is not runnable as is.  It needs to know how to find the library dependencies.  You can pack the dependencies into the JAR with 7-zip.  So, open output.jar in 7-zip.
  2. Add/drag the contents of foo_lib (from step 2 in the first section) into the root level of the JAR (e.g. they should be in the same directory META-INF is in).
  3. Extract the other JAR from the first section (see step 4) into a temp directory (e.g. temp).
  4. In output.jar, clobber the file "META-INF/MANIFEST.MF" with the file from "temp/META-INF/MANIFEST.MF".
  5. In output.jar, add the directory "org/eclipse" from "temp/org/eclipse".
  6. Confirm the JAR is runnable: java -jar output.jar

Wrap your JAR in a Windows executable

  1. Use Launch4j to wrap your JAR.  Open the Launch4j GUI.
  2. Add your output file: out.exe
  3. Add your input JAR file: output.jar
  4. In the JRE tab, set the Min JRE version (e.g. 1.6.0)
  5. Click the gear icon to build the wrapper.
  6. Confirm the binary works.  High Fives all Around!
 
POSTED 2012-07-07 22:05:59 CATEGORY ENGINEERING SOFTWARE TAGS DEVELOPMENT JAVA LAUNCH4J LIBGDX JAR PROGUARD IMPATIENT GUIDE
LIBGDX: CREATING MULTIPLE STAGES DEGRADES PERFORMACE
While working on a game using the ever wonderful libgdx, I noticed slow performance when I was using two different Stages.  One stage was the game HUD and the other served as a poorman's dialog.I didn't notice the choppy performance hiccups until after I called draw on the second stage then returned back to the original HUD stage.I had something like the following (WARNING: pseudocode):

Stage hud = new Stage(); Stage dlg = new Stage(); ... hud.addActor(widget1); hud.addActor(widget2); dlg.addActor(widget3); ... void render() {   if (show == HUD) {     hud.draw();   } else {     dlg.draw();   } }

After seeing the problem, I had a suspicion the performance degrades when swapping between Stages and I still am not sure why it does so.  At any rate, I fixed the problem by using only one Stage then adding and removing Actors when needed.So, the code ended up something like this: Stage ui = new Stage(); Vector hud = new Vector(); hud.add(widget1); hud.add(widget2); Vector dlg = new Vector(); dlg.add(widget3); ...// Call show() when you want to switch context void show(what) {   ui.clear();   Vector widges = hud;   if (what == DLG) {     widges = dlg;   }   Iterator iter = widges.iterator();   while (iter.hasNext()) {     ui.addActor(iter.next());   } }void render() {   ui.draw(); } That cleared up my performance trouble.Find out more about libgdx at Badlogic Games
POSTED 2011-12-21 11:16:07 CATEGORY ENGINEERING TAGS GAME LIBGDX PERFORMANCE
prev page
• • •
next page