Scala on Android



@jberkel

/me


  • From Java to Ruby and back
  • Work on the official SoundCloud Android app
  • <3 Android open source
  • Would love to see more people use Scala on Android

Quick Android recap

  • From 1.0 to 4.0 in < 4 years
  • 700k activations every day
  • 10 billion app downloads (Dec 2011)

Basics

  • at the core: Linux + DalvikVM (not JVM!)
  • Officially supported: Java + C/C++ (NDK)

What's this Dalvik thing?


  • Dalvik bytecode != JVM bytecode
  • optimised for smaller devices
  • one VM per process (forked)
  • used to be very slow, JIT since 2.2

Not just Java

Why Scala on Android?


Android is Java framework design, ca. 2001


public class MyActivity extends android.app.Activity {

  @Override public void onCreate(Bundle b) {
    super.onCreate(b);
    BatteryHelper.initialize(this);
  }
}

Traits to split concerns


trait BatteryAware extends Activity {
  override def onCreate(b: Bundle) {
    // register battery handler
  }
  override def onPause() { ... }
}

class MyActivity extends Activity
  with BatteryAware
  with Logger {
}

Android & APIs

actually not so bad.

But sometimes awkward


// database lookup
Cursor c = resolver.query(...);
List<MyModel> list = new ArrayList<MyModel>();
// iterate over rows and create objects
while (c != null && c.moveToNext()) {
    list.add(MyModel.fromCursor(c));
}
if (c != null) c.close(); // release resource
return list;

ugh.

Let's make a nice functional Cursor


class NiceCursor(c: Cursor) extends Iterable[Cursor] {
  def iterator = new Iterator[Cursor] {
    def hasNext = c.getCount > 0 && !c.isLast
    def next() = { c.moveToNext(); c }
  }
}
implicit def NiceCursor(c: Cursor) = new NiceCursor(c)

and a better query interface

def query[T](uri: Uri)(fun: Cursor => T) = {
    val cursor = resolver.query(uri)
    try {
      fun(cursor)
    } finally {
      cursor.close()
    }
}
// that's better
val list = query(...)(_.map(MyModel.fromCursor(_)))

shorter callbacks

...pimp my GpsStatusListeners


locationManager.addGpsStatusListener(
  new GpsStatusListener() {
    public void onGpsStatusChanged(int evt) {
      System.out.printn(evt)
    }
  }
}
// vs.
locationManager.addGpsStatusListener(println(_))

Testing on Android

...sucks.


  • slow
  • many devices & versions
  • verbose test code

Scala for testing


ScalaMock

  • Uses a compiler plugin to generate mocks
  • Currently not able to mock Android core classes


github.com/paulbutcher/scalamock

robolectric / robospecs


  • Testable reimplementation of the SDK (don't ask)
  • Classloading trickery allow test execution on development machine
  • Works with specs2 and ScalaTest


github.com/pivotal/robolectric github.com/jbrechtel/robospecs

ScalaTest Example

lazy val track = ...

it should "insert an read back a track" in {
  val uri = provider.insert(Content.TRACKS.uri,
    track.buildContentValues())

  val read = query(uri, 1)(_.map(new Track(_))).head
  read.id should equal(track.id)
  read.title should equal(track.title)
  // etc.
}

Problems with Scala

many runtime assumptions based on JVM:

  • object allocation is cheap
  • efficient and fast GC


However, Android developer guidelines say:

Avoid Creating Unnecessary Objects

But performance is improving (parallel GC, JIT etc)

Problems #2

Scala programs need a runtime lib


$ ls -lh scala-library.jar
rw-r--r--  jan  wheel  8.5M 24 May  2011 scala-library.jar

$ jar tfv scala-library.jar | wc -l
  5480

Solution

  • use proguard / treeshaker before dexing
  • preinstall Scala libs on the phone

Proguard is slow, long dev cycles

Only run it before releasing

Problems #3

Scala / Java / Android interop problems

issues.scala-lang.org/browse/SI-4620

But Android is "open" (sometimes...)

android-review.googlesource.com/#/c/30900/

Parcelables

public class Foo {
  public static final Parcelable.Creator<Foo> CREATOR =
    new Parcelable.Creator<Foo>() {
      public Foo createFromParcel(Parcel in) {
          return new Foo(in);
      }

      public Foo[] newArray(int size) {
          return new Foo[size];
      }
  };
 }

You cannot do this in Scala!

When to use Scala


  • Suitable for most types of apps
  • But not good for realtime (i.e. games)
  • Lack of experience / libraries
  • If unsure, start with Scala tests

Tools

  • sbt-android-plugin
  • Intellij IDEA CE (fsc + Scala / Android facets)
  • positronic lib

How to get started


Demo time...