Skip to content

Instantly share code, notes, and snippets.

@sleepynate
Last active November 8, 2016 12:08
Show Gist options
  • Save sleepynate/5518389 to your computer and use it in GitHub Desktop.
Save sleepynate/5518389 to your computer and use it in GitHub Desktop.
Androids Under the Stairs: Mobile Scala

Blurb

Scala is a sometimes-intimidating programming language that is Object-Oriented, functional, statically typed and wicked badass. It comes batteries-included, featuring a rich collections API, Type inference, and dead-simple concurrency. Perhaps most poignantly though, it's also a 30MB JVM library that can compile to dalvik.

Come take a heads first dive into crafting an APK with glorious, nutritionally fit Scala. We'll see how we can get more power from less code when wrangling the Android framework to do your bidding. We'll even play with all the basic tools that make this stop seeming like a daunting task and start seeming more like a walk up the stairs.

Demo

  • I'm nathan dotz
  • I work at Detroit Labs
  • I'm @nathandotz on twitter
  • I'm @sleepynate github and app.net

Why Scala?

At Detroit Labs, we have two things we do very often

  1. write mobile apps
  2. write services for those mobile apps

Ruby is meh

We used to write a lot of our services in Ruby, but as easy as Ruby is, it is often not the best solution. Concurrency is a pain in the butt, it's generally slower under load than other languages, and a lack of static typing makes for a lot of error-checking code in what should otherwise me straightforward 1-type-allowed requests or responses.

Scala is not meh

Scala, on the other hand, addresses nearly all of those issues cleanly, and while it appears somewhat scary from the outside, is actually a pleasant mid-ground between nice, fast services written in something clunky like Java and something more pleasant to write in like Ruby or Python. Scala doesn't "feel" like a traditionally statically-typed object-oriented language. More impressively, Scala brings functional paradigms to the table, which are all too apt for writing web services.

Scala-Android FUD

After being pleased that we had Scala in production for some web services, it was only natural to ask: "Where else could we apply this handy JVM language?" The answer of course was right in front of us -- we write for dalvik every day on Android.

I remember reading years ago about how slow Scala was on Android and "if only someone would put in some effort maybe some day it will be usable". Luckily, those years have passed and the community has figured out that a combination of updates to Scala, some tuning with ProGuard, and good old-fashioned trial-and-error, using Scala on Android actually incurs a rather low overhead and dramatically decreases the amount of code required for many mundane problems.

Prerequisite stuff

(a.k.a stuff we won't be covering but you'll need)

  1. Android SDK installed
  2. Scala 2.10 installed
  3. Maven-deployed Android SDK via maven-android-sdk-deployer
  4. A template project to make life easier

Goals

We'll be writing a simple app to check an RSS feed. Such an app already exists implemented in Java on github. We want to create an activity that contains a list view showing our the title of each RSS item, which when clicked, opens the default URL handler and displays the webpage. Simple, right? Well, despite it being a rather simple task, we can see from the rather sparsely-implemented Java version that this still requires nearly 200 lines of production code with a scant 100 lines of tests.

We should be able to accomplish the same thing in Scala in about 1/3 the number of lines and (in my very opinionated view) much more elegantly to boot.

Scaffolding

To get started, we'll use an existing template project. Simply clone the repository locally, and if you so choose, delete the .git directory to prepare for uploading it to github yourself.

$ git clone https://github.com/sleepynate/scala-on-android-template-project.git
$ cp -r scala-on-android-template-project androids-under-the-stairs
$ rm -rf androids-under-the-stairs/.git

This repository is forked from Mr. Rohan Singh's hard work, and includes some basic instructions on how to get maven set up for Android, as well as including a pre-made pom.xml already tuned for Scala development on Android.

What's in the box

Let's do a quick run-down of the important things in our project already:

$ tree androids-under-the-stairs

├── AndroidManifest.xml
├── pom.xml
├── proguard.cfg
├── res
│   ├── layout
│   │   └── main.xml
│   └── values
│       └── strings.xml
├── scala-test.keystore
└── src
    └── main
        └── scala
            └── HelloAndroidActivity.scala

AndroidManifest.xml, res/ and *.keystore we should know and recognize. Nothing special there. We also have a pom.xml, which has our maven configuration already set up with plugins for building Scala and Android, as well as running ProGuard on our application.

Speaking of, there's also a proguard.cfg which is tuned to keep the most important aspects of Scala and Android that we are likely to extend and remove the rest of the unused Scala classes, leaving us with (in optimal cases) around 800kb of overhead.

Finally, we have a source folder set up in standard maven style with the top level of our packages located at src/main/scala. Note though, that despite using the package name com.detroitlabs.thinks.you.are.cool, scala does not require us to maintain a directory structure

To prove to ourselves that Scala will actually build and deploy to Android, we can launch our emulator and deploy the demo app to it.

$ cd androids-under-the-stairs
$ $ANDROID_HOME/tools/emulator -avd demo &
$ mvn install android:deploy android:run

After downloading the entirity of the internet and taking a brief sightseeing orbital tour of Mars, maven builds an APK, deploys it to our emulator, and runs it, showing us the squares of the first few natural numbers, because gosh-darnit math is swell.

Default Activity

Looking at our sole source file, even if we've never seen Scala before, it should look rather familiar. We can see a class which extends android.app.Activity and overrides onCreate and onResume. Also, we can see some familiar functionality in Android such as setContentView, findViewById, our compiled R class, and TextView being used almost exactly as they would standard Java for Android.

Let's write some Scala!

Just kidding! Let's edit some XML. According to the unit tests provided with the sample app, we need a list view to stick items in, so let's make a quick change to our layout.

In res/layout/main.xml, we replace the <TextView … /> tag with:

<ListView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:id="@+id/feed_list"
    />

Naturally, this will cause our application to no longer compile. We can reconcile this back in our source file by replacing references to our TextView with our new ListView instead. Thus:

import android.widget.TextView
var helloTextView: Option[TextView] = None
helloTextView = Option(findViewById(R.id.helloTextView).asInstanceOf[TextView])

become:

import android.widget.ListView
var feedListView: Option[ListView] = None
feedListView = Option(findViewById(R.id.feed_list).asInstanceOf[ListView])

While this will compile, it doesn't really demonstrate our ListView, so let's deploy our old friend ArrayAdapter to show the squares we've calcuated until we're ready to figure out our XML parsing. So, we can replace:

val items = Array(1, 2, 3, 4, 5)
val output = items
  .map { i => i * i }
  .mkString(", ")

helloTextView map { _.setText(output) }

with:

import android.widget.ArrayAdapter
// …
val items = Array(1, 2, 3, 4, 5)
val output = items
  .map { i => (i * i).toString }

val adapter = new ArrayAdapter[String](this, android.R.layout.simple_list_item_1, output)
feedListView.get.setAdapter(adapter)

For those unfamiliar with functional programming, these few lines may seem dense and intimidating. However, after compiling and running the code as it is now, those familiar with Android might already be able to see some of the power of Scala's rich collections API shining through. What we've done here is to take an Array of 5 integers, and then map a function literal across them which at once both squares those numbers and then converts them to a string. A similar operation in Java might look like:

ArrayList<String> output = new ArrayList<String>();
int[] items = {1, 2, 3, 4, 5};
for(int i: items) {
    output.add(String.parseInt(i*i));
}

Compare this to a slightly more terse version of what we've included in our Android activity:

val output = Array(1, 2, 3, 4, 5).map { i => (i * i).toString }

There are quite a few nice things going on here which make Scala quite a pleasure to work with:

  1. Due to type inference, all of the 'noise' of the type declarations disappears. Thankfully, the Scala compiler is smart enough to figure these things out on its own.
  2. A robust collections API meant that we didn't have to manage the conversion of our collection from int[] to ArrayList<String> manually by extracting elements from one collection, converting their type and adding them to another.
  3. In tandem with the collections API, a functional approach meant that instead of dictating the steps taken to transform our collection, we defined a transformation and applied it to the collection as a whole.

Parsing XML

Now that we have a ListView to display our XML results, it's probably about time we look into actually retrieving XML to parse and place into said ListView. The approach taken with the standard XmlPullParser included with Android is, frankly, ugly as sin. The implementation is about as bare-bones as it gets and weighs in at a mere 50 lines. Luckily, much like the collections API, Scala includes a glorious XML library with an enjoyably sugary syntax.

Currently, we don't know much about that library or its API. Normally with Java, we'd probably sit down and read through several pages of Javadoc that showed up in a web search before trying out a few promising methods in a separate test implementation before realizing we had an entirely wrong approach while checking stack overflow. Scala saves us a significant amount of pain by allowing us to play around in the REPL.

Let's take a dive! Now, we could simply launch Scala's REPL by typing scala at the command line. However, sbt, the de facto build tool used in Scala projects, provides a "beefed-up" sbt console which provides extra functionality such as tab completion that is great for exploring new libraries. We'll use that instead to make life a little easier on ourselves as we explore this strange new world…

$ sbt console
[info] Loading global plugins from /Users/sleepynate/.sbt/plugins
[info] Set current project to default-d0f036 (in build file:/private/tmp/)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_43).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

Now, since we've handily looked up that the XML classes we need are located in the scala.xml package, we can freely load all the members of this package and explore! First though, let's take a look at what's available. we'll type scala.xml. at the REPL and hit tab, showing all the publically available members of the package:

scala> scala.xml.
Atom                          Attribute
Comment                       Document
// (…)
Unparsed                      UnprefixedAttribute
Utility                       XML
XercesClassName               Xhtml
dtd                           factory
include                       package
parsing                       persistent
pull                          transform

Wow, there's a lot there. However, the XML class looks promising. Let's import the packages members and see what it has for us, again using the tab-completion avalable in sbt console

scala> import scala.xml._
import scala.xml._

scala> XML.
adapter         asInstanceOf    encoding
isInstanceOf    lang            load
// (…)

The load method looks promising…

scala> XML.load

def load(is: java.io.InputStream): T
def load(reader: java.io.Reader): T
def load(source: org.xml.sax.InputSource): T
def load(sysID: String): T
def load(url: java.net.URL): T

Oh boy! Even without knowing much Scala, it looks like we've found an XML.load function that will take a URL, an InputStream, or other familiar Java objects. I wonder if it's as simple as supplying a string with our URL:

scala> XML.load("http://detroitlabs.tumblr.com/rss")
res0: scala.xml.Elem = <rss version="2.0" xmlns:dc=…

It would indeed appear that loading our XML from the web was as simple as that. Note too, that even though we didn't explicitly bind this result to a val, the REPL has done so for us, meaning we can reference this value later through the symbol res0

scala> res0
res1: scala.xml.Elem = <rss version="2.0" xmlns:dc=…

Using a similar method, with the joys of tab-completion, we can discover that our result has methods like toList, flatten and map, which indicate we've likely got a collection on our hands. We can even explore what methods might be available on a single element of our collection by simply evaluating the first object and checking its available methods:

scala> res0.head
res2: scala.xml.Node = <rss version="2.0" xmlns:dc=…

We can see the type has changed from Elem to Node, etc…

After some playing around with the functions available on our objects, we might end up with something like:

scala> (res0 \\ "item").map { n =>
  (n \ "title").text +" - "+ (n \ "link").text
}
res20: scala.collection.immutable.Seq[java.lang.String] =
List(Emochi - http://detroitlabs.tumblr.com/post/47561431779, Nerd …

So, it looks like we can decompose our XML by their node names using the \\ and \ functions. xmlobj \\ "item" gives us a collection of nodes, and node \ "title" gives us the value of the "title" tag from within a node. Armed with this knowledge we've gained by being valiant explorers, we can get back to our actual implementation in Android land.

Case Classes

Having discovered that we're interested in referring to some set of title/link combinations, it seems only suitable to create a container in which to store such pieces of data. In Java, we'd often create a bean for this, or if we were anticipating passing it around the Android ecosystem, defining a Parcelable class. Scala provides us a much more convenient method for such cases, and it is appropriately called the case class. We'll define one called RssItem to handle our data.

case class RssItem(title: String, link: String) {
  override def toString = title + " (" + link + ")"
}

A case class is a special class which defines an object that automatically exposes the parameters to its constructor, making it a great container. Taking this class back into our REPL, we can building a nice collection of our RssItems like so:

scala> for (item <- res0 \\ "item") yield RssItem((item\"title").text,
     |  (item\"link").text)
res25: scala.collection.immutable.Seq[RssItem] =
List(Emochi (http://detroitlabs.tumblr.com/post/47561431779), Nerd Nite …

Additionally, case classes give us some other freebies, such as defining apply, unapply, equals, and the toString method we've overridden, though we won't delve into those here.

Displaying our list

It appears we've got a solid parsing implementation. All that is left to do is to combine it with our display code and we'll have successfully ported our application to Scala.

First, let's add our RssItem class to our source file, as well as creating a function on our activity's companion object which fetches and parses our RSS items:

import scala.xml._

case class RssItem(title: String, link: String) {
  override def toString = title + " (" + link + ")"
}

object HelloAndroidActivity extends Activity {
  // (…)
  def fetchRss(url:String) = {
    val xml = XML.load(url)
    for (item <- xml \\ "item") yield {
      val title = (item \ "title").text
      val link = (item \ "link").text
      RssItem(title, link)
    }
  }
}

And naturally (because I know like any good Android Developer, you'd never forget this), we'll put the internet permission in our manifest:

  <!-- … -->
  <uses-permission android:name="android.permission.INTERNET" />
</manifest>

Now that we can load our list of RssItems, we can do a minimal implementation to simply get our list items displaying simply by changing the contents of our output val in onResume:

  override def onResume(): Unit = {
    super.onResume()

    val items = HelloAndroidActivity.fetchRss("http://detroitlabs.tumblr.com/rss")
    val output = items map { _.title }

    val adapter = new ArrayAdapter[String](this, android.R.layout.simple_list_item_1, output.toArray)
    feedListView.get.setAdapter(adapter)
  }

Our last bit of click functionality comes from the rather hackish method of setting an OnItemClickListener for our ListView which finds the appropriate item from the list and launches a browser based on its link val.

feedListView.get setOnItemClickListener(new OnItemClickListener() {
  override def onItemClick(parent: AdapterView[_], view:View, pos:Int, id:Long) {
    val browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(items.toArray.apply(pos).link));
    startActivity(browserIntent);
  }
})

Further Reading

The gents and ladies over at linkedin have been experimenting with Scala on Android internally, and have gone over some additional nice features available in Scala for Android, including implicit conversions and using the Actor model of concurrency instead of Android's AsyncTask model. Check out their article

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment