Saturday, 17 August 2013

Akka in Your Pocket

Although Akka is usually thought of in connection with massively scalable applications on the server side, it is a generally useful model for concurrency that can be deployed in an ordinary PC application or even a mobile app.

My Nexus 4 phone has four cores. If I have some CPU-intensive processing in an Android app, I observe that I can speed it up nearly 300% by spreading load across four actors. This can mean the difference between one second response time and quarter-second response time, quite noticeable to the user. Therefore, along with regular Scala futures, Akka is a useful tool that Scala provides to Android developers to make the most of their users' computing resources.

One tricky bit is getting the initial configuration correct with SBT. After some experimentation, I found that these Proguard settings, adapted from an early version of this project, work. (ProGuard shrinks and optimizes an application, but when you include a library like Akka, it needs to be reminded to keep certain classes using "keep options.")
      proguardOption in Android := 
        |-keep class com.typesafe.**
        |-keep class akka.**
        |-keep class scala.collection.immutable.StringLike {*;}
        |-keepclasseswithmembers class * {public <init>(java.lang.String,$Settings, akka.event.EventStream,,;}
        |-keepclasseswithmembers class * {public <init>(;}
        |-keep class scala.collection.SeqLike {public protected *;}
        |-keep public class * extends
        |-keep public class * extends
        |-keep public class * extends android.content.BroadcastReceiver
        |-keep public class * extends android.content.ContentProvider
        |-keep public class * extends android.view.View {public <init>(android.content.Context);
        | public <init>(android.content.Context, android.util.AttributeSet); public <init>
        | (android.content.Context, android.util.AttributeSet, int); public void set*(...);}
        |-keepclasseswithmembers class * {public <init>(android.content.Context, android.util.AttributeSet);}
        |-keepclasseswithmembers class * {public <init>(android.content.Context, android.util.AttributeSet, int);}
        |-keepclassmembers class * extends android.content.Context {public void *(android.view.View); public void *(android.view.MenuItem);}
        |-keepclassmembers class * implements android.os.Parcelable {static android.os.Parcelable$Creator CREATOR;}
        |-keepclassmembers class **.R$* {public static <fields>;}

Note: this has only been tested to work with Akka 2.1.0 and Scala 2.10.1. If you use other versions, you may have do some more tinkering/googling.

Saturday, 3 August 2013

Using 2.10 Futures in Android

In an Android app, it is usually necessary to run some tasks in the background so they don't block
the UI. The standard way to do this in Java code is with an AsyncTask. Basically you fill out the body of an AsyncTask with two parts: what needs to happen in the background, and what needs to happen in the foreground once that is complete. The return value of the background part is passed as a parameter to the foreground part:

Here is an example expressed in Scala, as always more concise than Java:

    new AsyncTask[Object, Object, String] {
      override def doInBackground(args: Object*): String = calculateScore
      override def onPostExecute(score: Int) { showScore(score) }

From a Scala perspective, it might occur to you that this is a bit similar to what a Future does. Futures are more general and concise, so in my opinion they are to be preferred. Indeed, following the example of Terry Lin, I tried to get the identical functionality as the above working using the Future trait in Scala 2.10, and it worked fine. The new code has this structure:

    future {
    } map {
      runOnUiThread(new Runnable { override def run() { showScore(score) } }))

This code is a bit shorter. The one thing to remember is that after the score is returned, we have
to remind Android to return control to the main thread -- that's what the runOnUiThread() is for.