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.
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:
- 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.
- 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").
- Hit "finish" and the wizard will generate your shiny new .jar file.
- 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:
- Run the GUI version of ProGuard: java -jar proguardgui.jar
- In the Input/Output tab, click "Add input..." and enter the first generated JAR path.
- Click "Add output..." and enter your output JAR... say (e.g. output.jar).
- 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.
- In the Obfuscation tab uncheck "Use mixed-case class names."
- Keep the default settings for now (you can tweak them later once you have this working).
- Start processing the JAR. In the Process tab click Process!
- If ProGuard finishes it will produce a nice obfuscated/shrunk JAR, output.jar.
Combine library dependencies into obfuscated JAR
- 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.
- 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).
- Extract the other JAR from the first section (see step 4) into a temp directory (e.g. temp).
- In output.jar, clobber the file "META-INF/MANIFEST.MF" with the file from "temp/META-INF/MANIFEST.MF".
- In output.jar, add the directory "org/eclipse" from "temp/org/eclipse".
- Confirm the JAR is runnable: java -jar output.jar
Wrap your JAR in a Windows executable
- Use Launch4j to wrap your JAR. Open the Launch4j GUI.
- Add your output file: out.exe
- Add your input JAR file: output.jar
- In the JRE tab, set the Min JRE version (e.g. 1.6.0)
- Click the gear icon to build the wrapper.
- Confirm the binary works. High Fives all Around!
While trying to configure Nginx and PHP so that the PHP files would run from the same directory as static files, the error "290 rewrite or internal redirection cycle while internally redirecting to "/index.php"..." persistently recurred.
Everything worked fine after changing nginx.conf to the following (inside server {):
root html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9123;
}
Source (in the "Also Good Section")
I found myself wanting to test some PHP on my local Windows machine and wanted a lightweight, fast solution besides Apache. My motivation was getting a test environment with a RESTful serverside api for backbone.js testing.
The resulting toolchain is comprised of:
- Nginx (a tiny, fast HTTP server)
- PHP
- Slim Framework (framework for writing RESTful APIs and applications)
Being no fan of PHP, getting this configuration was like putting salt on a wound, but it might be useful for those that want to get up and running in a very short time. I'll keep the explanations to a minimum to stay true to an Impatient Guide.
- Download and unzip nginx to c:\nginx-xxx\
- Download and unzip PHP to c:\nginx-xxx\php\
- Open c:\nginx-xxx\conf\nginx.config in a text editor and uncomment the .php (FastCGI) location config to look like this (take note of the bold items):
location ~ \.php$ {
root scripts;
fastcgi_pass 127.0.0.1:9123;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
try_files $uri $uri/ /index.php?$args;
}
- Download RunHiddenConsole.exe to c:\nginx-xxx\php\
- Create a batch file called start-php-fcgi.bat in c:\nginx-xxx\ with the following (take note of the bold items):
@ECHO OFF
ECHO Starting PHP FastCGI...
set PATH=C:\nginx-xxx\php;%PATH%
RunHiddenConsole.exe C:\nginx-xxx\php\php-cgi.exe -b 127.0.0.1:9123
- Your PHP files will be put in a directory called scripts so download the Slim Framework into c:\nginx-xxx\scripts\
- Start nginx and FastCGI via a console window:
> cd c:\nginx-xxx\
> start nginx.exe
> start-php-fcgi.bat
> curl.exe -i -X DELETE localhost/index.php/delete
HTTP/1.1 200 OK
Server: nginx/1.2.1
Date: Tue, 26 Jun 2012 00:35:50 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/5.4.4
This is a DELETE route
- That's it. You're done. High Five!!
One neat thing about this toolchain is you can zip everything up and make it portable. There is no need to mess with installers, environment variables, etc...
Confused? Visit PHPFastCGIOnWindows.