Typesafe Activator

Scala on Android with Macroid and Akka

Scala on Android with Macroid and Akka

Nick
Source
November 9, 2014
android macroid akka scala scaladays2014

This template features Macroid and Akka in a 100%-Scala Android application. It shows how to make user interfaces composable — with Macroid’s tweaks, snails and UI actions, and reactive — by using Akka to interconnect interface fragments.

How to get "Scala on Android with Macroid and Akka" on your computer

There are several ways to get this template.

Option 1: Choose macroid-akka-pingpong in the Typesafe Activator UI.

Already have Typesafe Activator (get it here)? Launch the UI then search for macroid-akka-pingpong in the list of templates.

Option 2: Download the macroid-akka-pingpong project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for macroid-akka-pingpong.

  1. Download the Template Bundle for "Scala on Android with Macroid and Akka"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Typesafe Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\macroid-akka-pingpong> activator ui 
    This will start Typesafe Activator and open this template in your browser.

Option 3: Create a macroid-akka-pingpong project from the command line

If you have Typesafe Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME macroid-akka-pingpong on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/macroid/macroid-akka-pingpong#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Prerequisites and installation

This template assumes that you have installed the Android SDK (and API 18) and have a basic understanding of Android programming (either in Java or Scala) and basic Scala knowledge.

In case you get stuck running the project or creating the IDE files, help can be found at Macroid’s Scala on Android page or the SBT Android plugin page.

Please note that Activator UI currently does not support running Android projects directly, so you’ll need to run the project from console: activator run.

At a glance

We revive Android development with a few interesting concepts and techniques. First, we utilize Macroid — a modular functional user interface DSL. Macroid is much more flexible than Android’s standard XML-based layout framework and makes it easy to program complex interactions in a declarative way. Second, we employ Akka actors to establish communication between different fragments of the user interface. Not only does this provide the lacking communication facilities, but it also helps to maintain proper threading and makes the interface “reactive”, which any large-scale application would benefit from.

What does this application do? Not much. Two parts of the screen are playing invisible ping-pong with each other, and you — the user — have to click on the rackets that look like normal buttons. But did we mention the code was fancy?

Android, Macroid and Akka 101

Android

The main units of Android programs are Activities, which correspond to application screens. You need to define activity’s visual layout (user interface) and behavior.

Another important concept is that of a Fragment — a reusable piece of user interface and/or behavior. Several fragments could be nested inside the activity’s layout.

Finally, classes such as Button or TextView are used to define the interfaces.

You can learn more about Android programming at http://developer.android.com.

Macroid

The four main concepts of Macroid used in this template are as follows:

  • Bricks — composable pieces of layouts, for example: w[Button]
  • Tweaks — composable modifiers of widget properties: text("Foo") + show
  • Snails — composable animations: delay(500) ++ fadeOut(400)
  • UI actions — composable chunks of UI code, that have to run on the UI thread (examples later)

You can learn more about Macroid at http://macroid.github.io.

Akka

Akka is an actor framework, which means that it is based around share-nothing entities (actors) that communicate by passing messages to each other.

The actor model is very robust and has been used in many high-performance reactive applications.

You can learn more about Akka at http://akka.io.

Code walkthrough

First, we define the build (build.sbt), include the SBT Android plugin (project/plugins.sbt) and Android manifest (src/main/AndroidManifest.xml). We also add a basic Akka configuration (src/main/resources/application.conf).

Next, we define our MainActivity (MainActivity.scala). Note the actor system initialization:

  // name of our actor system
  val actorSystemName = "pingpong"

  // players
  lazy val ping = actorSystem.actorOf(RacketActor.props, "ping")
  lazy val pong = actorSystem.actorOf(RacketActor.props, "pong")
This introduces two actors, which will play with each other.

Now let’s take a look at the RacketActor class (RacketActor.scala). Its main part is the message handling:

  def receive = receiveUi andThen {
    case Ball ⇒
      // boast
      log.debug("got the ball!")
      // save the opponent reference
      lastOpponent = Some(sender)
      // notify the UI
      withUi(f ⇒ f.receive)

    case Smash ⇒
      // boast
      log.debug("smash!!!")
      // send the ball to the opponent
      lastOpponent.foreach(_ ! Ball)
      // forget who it was
      lastOpponent = None
      
    case AttachUi(_) ⇒
      // if the ui is attached after
      // receiveing the ball, update it
      lastOpponent foreach { _ ⇒
        withUi(f ⇒ f.receive)
      }

    case DetachUi ⇒
      // do nothing
  }
The actor can receive two main types of messages. If it receives the ball, it notifies the UI so that a racket is shown. When the racket is pressed, the actor receives the Smash message, which makes it send the ball to its opponent. Additionally there are two messages that deal with connecting the actor to the user interface.

In our application each actor will be connected to a Fragment — a piece of user interface. The withUi(f ⇒ f.receive) line, which we saw above, obtains the attached fragment f and performs an UI action receive with it.

It’s time to look at the code of the fragment (RacketFragment.scala). The two fragments are actually identical, they are just attached to different actors. First, we declare some styles and effects for our rackets, like so:

  object Styles {
    // how racket looks
    def racket(implicit appCtx: AppContext) =
      hide + disable +
      text("SMASH") +
      TextSize.large +
      lp[FrameLayout](WRAP_CONTENT, WRAP_CONTENT, Gravity.CENTER)
  }
This example really showcases the power of Macroid, as we can combine various properties together with ease and take advantage of Scala’s modular aspect to achieve the desired namespacing.

Next, we teach our fragment how to connect to its actor:

  // get actor name from arguments
  lazy val actorName = getArguments.getString("name")

  // actor for this fragment
  lazy val actor = Some(actorSystem.actorSelection(s"/user/$actorName"))

Finally, we add the visual layout:

  l[FrameLayout](
    w[Button] <~ wire(racket) <~ Styles.racket <~ On.click(smash)
  )
and define a couple of UI actions. The first one shows the racket and, as we remember, is triggered from within the actor:
  def receive =
    racket <~ Effects.appear
The second one is triggered when the racket is clicked:
  def smash =
    // wait until the racket disappears
    (racket <~~ Effects.disappear) ~~
    // tell the actor to smash
    Ui(actor.foreach(_ ! RacketActor.Smash))
It sends a message to the actor, so that it can respond to its opponent. Note the use of double ~s: in a very concise way we were able to say that the fade out ( Effects.disappear) should take place, and only when it finishes the next action is taken. These asyncronous workflows are another remarkable feature of Macroid.

To conclude our overview, we need to include the fragments in the activity. No problem!

  val view = l[LinearLayout](
    // we pass a name for the actor, and id+tag for the fragment
    f[RacketFragment].pass("name" → "ping").framed(Id.ping, Tag.ping) <~ lps,
    f[RacketFragment].pass("name" → "pong").framed(Id.pong, Tag.pong) <~ lps
  ) <~ vertical

Conclusions

We have built an Android application with a simple reactive and modular UI thanks to the great Scala ecosystem. Make sure you follow the links to the projects used here to learn more and take advantage of their best features!

comments powered by Disqus