Saturday, December 8, 2012

Scala And Android Were Made For Each Other

I've been experimenting with Scala for a while now, and the more I use it, the more I like it.  Scala isn't exactly what I would call an approachable language, but if you are familiar with Java or C# and you slowly work your way into it, Scala is really a joy to work in.

I also like to write apps for Android.  Since Scala runs on the JVM, it runs on Android, too.  Recently I've started to think that Google made a mistake using Java for Android.  It probably helped Android gain apps and popularity since thousands of developers could install the Eclipse Android Plugin and start coding, but that's the thing - Coding on Android is very different than coding, say, a Web app.  It's even quite different than coding a desktop app.  And it is because of the way that things should be written on Android that Java seems to be a poor fit.  All those thousands of programmers started writing apps - and did it wrong.

I know.  I made tons of mistakes.  As I read more about the platform and become more familiar with how things ran, it is clear that a lot of what I did often wasn't a best practice.  Some of what I did was just wrong.

This is where I get to the title of this post.  I recently experimented and wrote an application for Android using Scala.  I have to say, after writing the application this way, I don't ever want to go back. It is my sincere belief that Scala is a much better fit for writing applications on Android than Java.  To prove my point, I am going to go through the app I wrote and show you the Scala code. I am going to assume if you are reading this that you already have some familiarity with Android development so I will refrain from showing the Java alternative.

I'm also going to point out that I am in no way a functional programming expert.  As I said, I use Scala as a better Java.  My code continues to become 'more functional', but I'm sure this is not idiomatic Scala.  That's okay by be, though, because things are just easier.  To me, that's the best measure of how good the code is.

Background

We recently had half our staff move to an office in San Francisco.  Our company has always moved at a rapid pace, but with the additional overhead of working with a team 2 hours difference and across the US, I was feeling like I was getting distracted. I wanted to find a management technique that was simple and that would help me stay on track.  I picked the Pomodoro technique - it was simple. It suited me. It was invented by a programmer, and there were several apps in the Play store for me to use. Of course, I wanted to write my own anyway.

For the purpose of this app, all you need to know is that a Pomodoro is a 25 minute block of time where you concentrate on doing work. Since this is a pretty straight forward app to write, though, I thought I would implement it in Scala to see how it went.

If you want to know more about the Pomodoro Technique, you can visit the official Pomodoro Technique page or Buy the book on Amazon (convenient link on the left).




Application Definition

Here are the things I want the application to do:
  • Allow me to start a Pomodoro (a 25 minute block of time)
  • Play the ticker sound while in a Pomodoro (for some reason, hearing this helps me stay on track)
  • Play a bell when the Pomodoro finishes
  • Allow me to cancel a Pomodoro if I have to
  • Keep the Pomodoro timer running, even if I close the application 
Here are the constituent parts of my application:
  • Activity - Main activity, only Activity.  It has the text that shows you the time left and a button that changes states.  The button will either start or stop the timer.
  • Service - A background service that will run the timer task - counting down 
If you've done any Android programming in the past, you realize that even these simple requirements will result in a slew of callbacks, threads, anonymous inner classes, binders, messengers...need I go on?  Okay, that was a bit of an exaggeration, but not as exaggerated as it seems.

Let's think about this for a moment. In order to prevent ANR warnings we need to keep things off the main thread, which, means any communication with our service should be run in separate threads.  Allowing the service to run separately from the Activity (and allow it to potentially be started and called from any activity, even someone else's) will require it to be started and bound so we'll need a Binder and then we'll need to implement its callbacks. Then communication should happen with Messages so we'll need a Messenger and we'll need to implement its callbacks. Oh, and if we want to update the UI so users know whats going on, we need to then run updates from background threads on the UI thread, which, requires a Runnable.

When you add all this up, it makes for lots of  boilerplate code.  Thankfully, the language features of Scala make this less verbose, easier to write,  more understandable, and ultimately easier to maintain.  It also makes it much easier to write code that will follow Android best practices.

Now that we know the what and the how, let's talk code.

The Code


We know we want a bound service that we can send messages to.  When you read the Android docs on it, you'll see that there is a abstract inner class that implements Handler and is passed to a Messenger that is returned in the Services onBind method.

Wow, that was a lot to say.  It is also a lot of boilerplate code.  I wanted to move this to a common class so it was out of my way.  In Java we might make an Abstract base class.  But wait, the Activity in our application also needs a Handler and a Messenger, and if we Make an AbstractService, we could not use that code in an Activity. If we make an AbstractActivity, we cannot use it for our Service.   Here is the first place where Scala is quite handy.  I was able to make a Trait.  Traits are very powerful and I'm using the one below as a 'better interface', which, might be under-utilizing this great feature.  It serves its purpose well, however.

1:  trait MessageReceiver {  
2:   implicit def toIncomingHandler(f: Message => Unit): Handler = new Handler() { override def handleMessage(message: Message) = f(message) }  
3:    
4:   val mMessenger = new Messenger({ m: Message => 
5:    onMessage(m)  
6:   })  
7:    
8:   def onMessage(m: Message)  
9:  }  

Notice that I also used an implicit to reduce the need to declare an abstract inner class to handle the message.  I probably didn't save any code, but I like the way this looks far better than the Java alternative.

Now that we have the MessageReceiver defined, let's define our Service.  Before I show you the code, here are some things I want you to notice about the class:

  • We override onMessage and implement it.  This is the only code in this class that is related to receiving a message.  All the boilerplate has been moved to the Trait which can be used by both Activity and Service classes.
  • The start method has an internal method called timer that is recursive. This also happens to be tail recursive, and Scala is smart enough to unwind this, so it won't grow my stack.  
  • We run the timer in a background thread.  Notice that all we have to do is surround it with a spawn{} block.  (I omitted the imports, but you need to import scala.concurrent.ops._ to do this)


1:  case class BackgroundTimer extends Service with MessageReceiver {  
2:     
3:   var millis = 0L;  
4:   /** Keeps track of all current registered clients. */  
5:   var mClients = List[Messenger]()  
6:   var currentTime = "0:00"  
7:    
8:   val mediaPlayer = new SoundPool(2, AudioManager.STREAM_MUSIC, 0)  
9:    
10:   var tickId: Int = -1  
11:   var alarmId: Int = -1  
12:   var tickStreamId = -1  
13:     
14:   override def onMessage(m:Message) = {  
15:    m.what match {  
16:     case BackgroundTimer.REGISTER =>
18:       mClients ::= m.replyTo  
19:      }  
20:     case BackgroundTimer.UNREGISTER =>
21:      if (m.replyTo != null) {  
22:       mClients = mClients.remove(messenger =>
23:        if (messenger == m.replyTo) {  
24:         true  
25:        } else {  
26:         false  
27:        })  
28:      }  
29:     case BackgroundTimer.START >  
30:      start()  
31:     case BackgroundTimer.STOP =>
32:      //don't play the alarm bell  
33:      stop(true)  
34:    }  
35:   }  
36:    
37:   override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {  
38:    Log.i("BAA", "Received start id " + startId + ": " + intent)  
39:    try {  
40:     tickId = mediaPlayer.load(this, R.raw.singletick, 1)  
41:     alarmId = mediaPlayer.load(this, R.raw.alarm, 1)  
42:    } catch {  
43:     case e: Exception => Log.d("BAA", e.getMessage())  
44:    }  
45:    Service.START_STICKY; // run until explicitly stopped.  
46:   }  
47:    
48:   /**  
49:    * Must override this to allow the service to bind to the Activity so we can start/stop timers  
50:    */  
51:   override def onBind(intent: Intent): IBinder = {  
52:    Log.i("BAA", "OnBind called")  
53:    return mMessenger.getBinder()  
54:   }  
55:    
56:   def start() = {  
57:    if (millis == 0) {  
58:     spawn {  
59:        
60:      var alarmStreamId = -1  
61:      try {  
62:       tickStreamId = mediaPlayer.play(tickId, 2.0f, 2.0f, 1, -1, 1.0f)  
63:      } catch {  
64:       case e: Exception => Log.d("BAA", e.getMessage())  
65:      }  
66:        
67:      def timer(millisLeft: Long): Unit = {  
68:       if (millis > 0) {  
69:        millis = millisLeft  
70:       }  
71:       val seconds = (millisLeft / 1000) % 60;  
72:       val minutes = ((millisLeft / (1000 * 60)) % 60);  
73:       currentTime = minutes + ":" + (if (seconds > 9) seconds else "0" + seconds)  
74:    
75:       if (millis > 0) {  
76:        mClients.foreach { messenger =>
77:         val response = Message.obtain(null, BackgroundTimer.TICK, 0, 0, currentTime)  
78:         messenger.send(response)  
79:        }  
80:        Thread.sleep(995);  
81:        timer(millisLeft - 995)  
82:       } else {  
83:        stop(false)  
84:          
85:       }  
86:      }  
87:      //now start it off...  
88:      millis = 1000 * 60 * 25  
89:      timer(millis)  
90:     }  
91:    }  
92:   }  
93:    
94:   def stop(forced:Boolean) = {  
95:    millis = 0  
96:    currentTime = "0:00"  
97:    mediaPlayer.stop(tickStreamId)  
98:    if(!forced)  
99:     mediaPlayer.play(alarmId, 0.2f, 0.2f, 1, 0, 1.0f)  
100:    mClients.foreach { messenger =>  
101:     val response = Message.obtain(null, BackgroundTimer.STOPPED, 0, 0, currentTime)  
102:     messenger.send(response)  
103:    }  
104:   }  
105:  }  
106:    
107:  /**  
108:   * Define an object to hold some statics  
109:   */  
110:  object BackgroundTimer {  
111:   val REGISTER = 1  
112:   val START = 2  
113:   val STOP = 3  
114:   val UNREGISTER = 4  
115:   val STARTED = 5  
116:   val STOPPED = 6  
117:   val TICK = 7  
118:  }   
119:    

I don't know about you, but this class looks very clean to me.  Almost all the code is directly related to what this class is intended to do with no messy boilerplate code for messaging or threading.  In fact, threading is now too easy -  just add a spawn block and done.  The 'business logic' is also very compact.  Since timer can be declared inside start, all the logic for actually running the timer is in one spot - no need to jump to a different section of code to see what's going on.

Want to try the app out?  Download Campari Pomodoro.

Next up, the Activity

No comments:

Post a Comment