The definitive reference for using Scala on android seems to be here: http://www.scala-lang.org/node/160
Unfortunately, all the references on using scala with android are based around Scala 2.7 and refer to a custom build android-library.jar, with a couple of cryptic references suggesting that this custom build isn't needed for later versions of android (I'm using 2.1 / API v7)
So... What are the steps needed to use Scala 2.8 in an android project? Preferably using eclipse and the Android tools that Google supplies for that IDE.
Edit:
My new way of doing this is to use my Eclipse plugin: https://github.com/banshee/AndroidProguardScala (the readme has instructions for installing and a pointer to a normal Eclipse update site).
The old answer still works, but the new way is just better.
[The old way]
My approach:
Use the normal Android/eclipse tools for creating a Java project.
Add a second project containing the Scala code. That way I get to keep the generated code for future reference (I'm new at both Android and Scala). This project can reference android.jar.
The scala project produces a jar file that's used in the java project
Use proguard to strip the library. I believe this avoids the need for the scala-android.jar that was used in 2.7
I haven't used this for anything more ambitious than hello, world though, so take it as more of a set of hints.
In the scala project, I add a builder (Builder > New) that's just a shell script called pguard in the root directory of the project containing:
#!/bin/sh
cd $1
PROGUARD=$HOME/dev/proguard/lib/proguard.jar
LIBS=
OUTPUT=lib/proguard.jar
rm -f $OUTPUT
AJAR=/Users/jamesmoore/dev/android-sdk-mac_86/platforms/android-7/android.jar
# java -jar $PROGUARD -injars 'bin:lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)' -outjar $OUTPUT -libraryjars #proguard.txt
java -Xmx1g -jar $PROGUARD -injars 'bin:lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)' -outjar $OUTPUT -libraryjars $AJAR #proguard.txt
The builder has Location set to:
${build_project}/pguard
And both working directory and arguments set to
${build_project}
Also in the root of the scala project, there's a proguard arguments file #proguard.txt:
-dontwarn
-dontoptimize
-dontobfuscate
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep public class com.banshee.** {
public protected *;
}
You'll want to change the -keep arguments to keep your own code, of course.
In the java project, I add the jar file that's produced by the scala project (I use lib/proguard.jar in the script above).
Don't add the scala project as a required project in the java project's build path, though. That will add the scala class files in addition to the jar file and confuse dex (since it'll get both the .class files and the same things in the jar). As far as I can tell, Eclipse will build everything in the workspace, so when you hit the go button, both projects get built.
After much investigation, it really does look like Proguard is essential to keep the size and speed of deploying the application to reasonable levels.
Sadly, there is no suitable way to embed proguard as a build step. Using scripts might be a possibility, but I also need to support Windows, Linux and OSX environments.
I was also unsure about the twin-project solution, as it prevented Scala code from using the generated resources file R.java, which I wanted to be able to do.
In the end, I was able to make both SBT and Ant build an android 2.1 application using Scala 2.8. Ant was the favourite final solution as it works with the same file organisation as Android's eclipse plugin. I've written up the solution here: http://scala-ide.assembla.com/wiki/show/ae55a-oWSr36hpeJe5avMc/Developing_for_Android
Eclipse then launches Ant as an external tool to package and install the application.
I'm now using a modification of my previous answer to run on Windows: just move everything into #proguard_windows.txt so you don't have to worry about running as a script.
My new #proguard_windows.txt looks like:
-injars bin;lib/scala-library.jar(!META-INF/MANIFEST.MF,!library.properties)
-outjar gen/scandroid.jar
-libraryjars lib/android.jar
-dontwarn
-dontoptimize
-dontobfuscate
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep public class com.banshee.** { public protected *; }
-keep public class org.xml.sax.EntityResolver { public protected *; }
And note that in windows, you need to use a semicolon for -injars. Nasty.
The builder looks like this:
(running cygwin here, so the cat option path takes a slash)
James#Greine:/cygdrive/c/Users/james/workspace/Scala2$ cat .externalToolBuilders/proguard.launch
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="C:\Windows\System32\java.exe"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="-Xmx1g -jar lib/proguard.jar #proguard_windows.txt"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${build_project}"/>
</launchConfiguration>
You'll want to put this in .externalToolBuilders/proguard.launch.
The interesting thing here is that it's just a java command, not any kind of shell script, so it's fairly easy to port between Windows/Mac (and I'm assuming Linux, but haven't done that yet), since you're just changing the location of the actual java binary.
(Submitting this as a new answer because it's a bit different than the one that got marked as the correct(ish) answer)
I have been using Proguard to treeshake the Scala library in Eclipse/ADT builds, but it leaves a lot to be desired. It's slow and it messes up the other .class files, so you have to rebuild the project more often than not.
Inspired by these questions and some of the issues in the Android issue tracker, I have made another tool (based on Jar Jar Links) which I now use to treeshake the scala library. With this build step I'm actually fairly happy with the whole Eclipse+ADT Scala on Android situation. Maybe it'll work for you too, give it a spin, it's available from http://code.google.com/p/treeshaker/
If you are comfortable using the Gradle build tool, then the Android plugin for Gradle makes this whole process extremely simple. See: https://github.com/jvoegele/gradle-android-plugin/wiki
Related
I have exactly the same issue as was answered here for a Maven Android project.
However I have to work with a command-line Android build system, which uses a build.xml in the project ( which is a library project.)
I tried a toy project with simplexml in Eclipse
and it all seem to work automagically. However, I've no idea what I should put in the
command line build.xml file to achieve the same thing... I've only a passing acquaintance with
ant....
thanks
Android: error including/repacking dependencies which reference javax core classes
I had another look at this code, and I see that it was the "proguard" stuff that
was failing.
So I added this to proguard.cfg
and the code at least compiled then. Haven't had a chance to see if it works yet.
-dontwarn javax.xml.stream.**
I have a libgdx android project in eclipse which I've added scala and AndroidProguardScala natures to. This was working great, but suddenly eclipse has started giving me the warning "More than one Scala library found on the build path". I can still build and install the project on a device, but it exits immediately with NoClassDefFoundError: scala.Tuple2$mcZZ$sp, so it looks like the Scala library isn't being included at all.
My project seems to have the Scala library both under "Scala Library [2.10.1]" (as "scala-library.jar") and under "Android Dependencies" (as "scala_library.min.jar") so I guess that the Proguard output is conflicting with the original library, but I have no idea why this has happened or how to fix it.
At this point I could just make a new project and copy the source files over, but it would be great if anyone could shed some light on this.
Edit: After some experimentation it turns out that this code (from within a method of a trait) seems to be causing the problem.
acckeys match {
case (true, false) => acceleration.set(0, accel).rotate(rotation)
case (false, true) => acceleration.set(0, -accel).rotate(rotation)
case _ => acceleration.set(0, 0)
}
(where acckeys is a function returning a tuple and acceleration is a vector)
The multiple libraries warning is still there, so I guess that isn't related to to the problem. Luckily, I don't actually need to reference this code from the android version of the project, but it would still be useful to know why this code compiles but fails to run.
I know exactly what issue you are having. This has to do with the AndroidProgaurdScala not recognizing the class files you have in your main project. The result is, any classes which are defined in the android project are included, but any 'new' ones, such as your tuple, will be missing and because of that you get the wonderful NoClassDefFoundError. I haven't encountered the warning with "More than one Scala library found on the build path", but it probably means you added the scala nature to the project and also added the library.jar on top of it as well. Try to undo your configuration and follow what I have below; otherwise, set up a new project with the steps below and copy your source over.
I found the Scala Nature and Android Proguard Scala are a bit of a pain to fix after they have run a muck. I'm not 100% sure you will be able to salvage an existing project, but we can try and see what happens. First, remove all scala related natures from your projects and get them back into their initial state they would be in for just a plain old Java project.
Let's walk through setting up libGDX with Scala and see where things went wrong:
Make sure your environment is setup correctly. (Note: you will need to get the latest versions)
Set up the libGDX project as normal. (Or use an existing project in your case)
Right-click the android project, "Add Scala nature" then "Add AndroidProgaurdScala nature"
Right-click the main project, "Add Scala nature"
Right-click the desktop project, "Add Scala nature"
Now, this is probably where you have gotten to already, but there is still the issue of AndroidProgaurdScala not knowing what classes you have in your main project. It appears to overlook included projects and doesn't add them to the scala_library.min.jar. To fix this I have found one solution that works well:
Right-click Android Project > Properties > Java Build Path
Go to Projects tab, remove the main project. I know this is counterintuitive, but trust me.
Go to source tab > Link Source...
Navigate and select the "src" folder from your main project
Press OK
Clean and rebuild the projects.
This setup I have tested and used extensively without any issues. Both Android and Desktop versions are happy and work without any complaints with Scala or mixed code. The android project is now fully aware of all the Scala classes you are using in the main project because the source is now copied into the build for the android project. (i.e. No NoClassDefFoundError should appear with anything Scala related).
I've gotten a similar message when my Proguard configuration file has a problem. You might check:
Is Proguard being run
Your config file has: -keep public class **.YourPackage.** or something like it
Your config file is modified for the Scala libraries, eg.
## SCALA SETTINGS
-dontwarn **$$anonfun$*
-dontwarn scala.android.**
-dontwarn scala.beans.ScalaBeanInfo
-dontwarn scala.collection.generic.GenTraversableFactory
-dontwarn scala.collection.immutable.RedBlack$Empty
-dontwarn scala.concurrent.forkjoin.**
-dontwarn scala.reflect.**
-dontwarn scala.sys.process.**
-dontwarn scala.tools.**,plugintemplate.**
#(org.xml.sax.EntityResolver)Class.forName(variable).newInstance()
-dontnote org.xml.sax.EntityResolver
#(org.apache.james.mime4j.storage.StorageProvider)Class.forName(variable).newInstance()
-dontnote org.apache.james.mime4j.storage.DefaultStorageProvider
-dontnote scala.android.app.Activity
-keep class scala.android.package**
-keep class * extends scala.android.app.Activity
## Fixes ==> Warning: ... can't find referenced class sun.misc.Unsafe
-libraryjars D:\Program Files\Apache\m2\repository\com\google\code\findbugs\jsr305\2.0.1\jsr305-2.0.1.jar
-dontwarn sun.misc.Unsafe
-keep class * extends scala.runtime.MethodCache {
public ;
}
-keepclassmembers class * {
** MODULE$;
}
-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinPool {
long eventCount;
int workerCounts;
int runControl;
scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack;
scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack;
}
-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinWorkerThread {
int base;
int sp;
int runState;
}
-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinTask {
int status;
}
-keepclassmembernames class scala.concurrent.forkjoin.LinkedTransferQueue {
scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head;
scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail;
scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe;
}
First of all, i'm new on ProGuard, but i read some tutorials and i know that the best way to use it on android is the one described on android.developer guide.
im trying to obfuscate the code of my new Android app with ProGuard. For that i enter this website: http://developer.android.com/tools/help/proguard.html#enabling
But it tells: "hen you create an Android project, a proguard.cfg file is automatically generated in the root directory of the project."
That file does not exist on my project root directory, so i dont know how to continue. My Android app is for api level 4, 1.6, so, is it possible that this is a problem for using ProGuard?
How can i use proguard with an app for api 4 (android 1.6)
Thanks a lot
You need a proguard.cfg File. You can either create it by hand or use the proguard-GUI (> java -jar proguardgui.jar). Using the GUI makes some things easier, but a basic understandig of proguard and obfuscating is still required. It isn't very comfortable to use the gui for obfuscating your release apk, so proviging the config-file and using the SDKs Build tools is still the best way to go.
The ACRA Documentation features an Example for a proguard.cfg http://code.google.com/p/acra/wiki/ACRAProGuardHowTo most of the stuff is ACRA related and can be ommited if you don't use ACRA in your project.
I Don't know if the SDK is supposed to create an proguard.cfg. If never seen one which was created automaticaly, so i suggest you go with the file as supplied by the acra-guys for a starting point.
Make sure that you are using the latest Android SDK. You can check this with the standard android application from the SDK.
Then make sure that your project directory is up-to-date too, by typing
adb update project -p MyProjectDirectory
(from the command-line, with the proper path to the directory of your project). This should create a ProGuard configuration file, which is called proguard-project.txt in recent releases of the SDK.
You can then enable ProGuard by uncommenting the proper line in project.properties.
I have been working on one project which is too complex and contain very much space with so many images and Java files as well.
Somewhere I have read about the proguard which optimizes the code.
I have used it, but it's still does not have an effect on my final APK file.
It might be I have made a mistake somewhere. I have the following this like http://developer.android.com/guide/developing/tools/proguard.html.
How can I optimize my code?
You can add it to the default.properties. I've been adding manually without having a problem so far.
If you add the line:
proguard.config=proguard.cfg
As said it will only use ProGuard when exporting signed application (Android Tools => Export Signed Application)
If you start the project with the SDK before Android 2.3 the proguard.cfg file will not be created (next to default.properties as in 2.3>).
To enable automatic creation of it, just simply update to the SDK of Android 2.3 and create a new project with existing sources (which are the sources of the project you currently have).
Automagically the proguard.cfg fill will be created.
Without optimizations the compiler produces very dumb code - each command is compiled in a very straightforward manner, so that it does the intended thing.
The Debug builds have optimizations disabled by default, because without the optimizations the produced executable matches the source code in a straightforward manner.
Please refer this one
From documentation:
ProGuard is integrated into the Android build system, so you do not have to invoke it manually. ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode.
After running headfirst into as many problems as there are permutations of the set of Android command-line tools, I finally managed to compile a mix of Scala and Java source code into a usable apk.
As many suggest, I used proguard to squeeze the Scala library through the dex tool. The problem is this:
BUILD SUCCESSFUL
Total time: 1 minute 29 seconds
One minute and a half. We're talking about an application with Hello-World complexity here. I don't think I can develop like that. I'm gonna need to take meditation classes.
This is the proguard configuration:
-injars ${out.absolute.dir}/classes:${scala-library.jar}(!META-INF/MANIFEST.MF,!library.properties)
-outjars ${out.absolute.dir}/classes.min.jar
-libraryjars ${android.jar}
-dontwarn
-dontoptimize
-dontobfuscate
-keep public class * extends android.app.Activity
Is there a way to speed up the proguard step?
Edit: I'm running this in a pretty decent dual-core, 3GB ram machine, on top of 64-bit Linux. A run of ant compile (scalac/javac) takes 3 seconds. A full ant install takes 1:30, as described above. It's the proguard step that "freezes", according to the output, most likely because of the scala/android runtime lib sizes.
Working via android Ant builds is probably not the right way to go about this.
The current "best advice" is to use SBT with the
proguard (https://github.com/siasia/xsbt-proguard-plugin) and
android (https://github.com/jberkel/android-plugin)
plugins.
ProGuard takes a lot longer to shrink the Scala 2.9.1 library than the Scala 2.8.1 library (54 seconds vs. 13 seconds, for 8.5 MB vs. 6.2 MB). Either the structure of the library classes has changed fundamentally, or some new classes are causing excessive computations. I'll have to figure out if ProGuard or its configuration can be improved for this case. For now, you might be able to work with Scala 2.8.1.
I'm assuming your ProGuard configuration also contains the required options for Android and for Scala, as discussed in the ProGuard manual. If you are using the regular Android build process, the input (classes, libs) and libraries (android.jar) are already specified for you in the Ant build file, and you don't need to specify them again in the ProGuard configuration file. Reading them twice will just take time and generate many warnings (which you have switched off completely -- it's safer to switch them off selectively).
If you have a rooted phone and only want to test on this you can install the scala libs directly on your phone so you don't need the proguard step anymore:
https://github.com/jrudolph/scala-android-libs