Making of: Translate Bubble in Scala on Android

Making of: Translate Bubble in Scala on Android

Five months ago, I became a dad. During the following two weeks, I was at home adapting to my new life, but I was also able to take a little time for coding. At that time, I was only coding in Scala on the backend, but I needed write code on Android. In my mind, I had an idea for an application that I wanted to create, so I made a proof of concept in Java and Translate Bubble was born.

When I got back to the office after my paternity leave was over, I showed the application to my coworkers and they liked it, but we continued working and forgot about it. Two months later, when we started to get more interested in Scala development on Android, I remembered Translate Bubble and realized that I could convert it to Scala.

While I was working over a weekend, I created a first implementation using only Scala. Here’s my experience of the process and what I learned as a result.

Converting my code to Scala

When I started to create the app with Java, the first decision I had to make was around the libraries that I should use. It’s pretty typical on Android to use several libraries in your projects. Aside from the compatibility libraries, I chose the following three libraries for my app:

  • Dagger for dependency injection on Android with Java
  • android-priority-jobqueue for running jobs in the background. Many people hate AsyncTasks and Loaders and we need libraries to work with async tasks
  • EventBus for simplifying communication between Activities, Fragments, etc.

Besides these libraries, I had to also take into consideration:

  • I needed BaseActivity and BaseService. It’s typical that you’ll need these classes to add common tasks to your Activity or Service
  • I created different managers to run common tasks like ClipboardManager or LaunchNotificationManager

However, once I started to convert the project to Scala, I realized that I could remove all of these libraries because I didn’t need them.

I started to use the Cake Pattern instead of Dagger for Dependency Injection. The Cake Pattern solves dependencies between components and is a potential solution for DI on Android when using Scala. There’s a lot of information available about the Cake Pattern on the web, but we recommend this post by Jonas Bonér. Finally, I added all of my Dagger modules and services using the Cake Pattern.

To address the problem of async calls in Android, with Scala we have Future. Future solves all of the problems that come with asynchronous calls on Android.

In Scala, you can also use Traits, which are similar to Java interfaces, but Scala allows traits to be partially implemented. As a result, you can focus your code on your functionalities and forego using BaseActivity or BaseService, so your code can be clearer and simpler.

Show me the code!

One frequent problem in Android is the NullPointerExceptions. When you’re coding in Java, your code is very defensive and you need to check if your variables are null or you’ll encounter crashes in your application.

For example, this is the code in Android when you want to get the text copied to the clipboard:

public CharSequence getText() {
    ClipData clip = clipboard.getPrimaryClip();
    if (clip != null && clip.getItemCount() > 0) {
        CharSequence aux = clip.getItemAt(0).getText();
        if (aux != null && aux.length() > 0 ) {
            return aux;
        }
    }
    return null;
}

Scala has a much more powerful solution based on the Option type. If you have a val test : Option[String] this means that the result is either None or Some[String]. If you call map(f) on a None, it returns None and the function is never evaluated, but if your variable contains information, it returns the contained value transformed by your function.

For example, here is the previous code written in Scala:

 def getText(): Option[String] =
    Option(clipboardManager.getPrimaryClip) map (_.getItemAt(0)) flatMap (_.getText)

This is a simple model, but you can use the Option type in your Views. As an example we are using a library called Macroid that is focused on GUI and helps you create user interfaces using Scala Macros (you can learn more about Macros in our previus post, Scala Macros - Annotate Your Case Classes). Using the Macroid library you can easily wrap your views in slots that behave like Option, if you manage your views correctly, you will reduce the number of NullPointerExceptions associated with accesses to views that are not initialized in Android.

Another common problem in Android is the Context. You have to be constantly passing around the context to your views, services, utilities, etc. For example, here we showing the constructors in our old managers in Java:

...
public LaunchNotificationManager(Context context) {
    notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    this.context = context;
}
...
public ClipManager(Context context) {
    this.clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
}

Scala offers us a very powerful tool called implicits. You can declare an implicit val which is automatically passed to methods calls like any other parameter and the compiler will try fill them automatically in the scope of the call site.

implicit lazy val context = getApplicationContext

When you call the method, you don’t have to include the implicit parameters in your calls

...
def clipboardManager(implicit context:Context): ClipboardManager = {
  context.getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
}
...
def clipboardManager(implicit context:Context): ClipboardManager = {
  context.getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
}

Macroid has many extensions to do the above in a even cleaner way by adding implicit parameters for your contexts. You can read about Contexts in Macroid to better understand the benefits of using implicits in your code.

And the last code that I want to demonstrate is about Futures. As we mentioned previously, Android has a lot of problems when you are working with asynchronous tasks. You can use AsyncTasks or Loaders, but we used to use android-priority-jobqueue for async operations. You have to create Jobs and use an EventBus to communicate with your activities, fragments, etc. When you use Future, you can run a block of code on another thread and execute an action once you get the result. The most interesting part is that you can concatenate several Futures inside a for comprehension and you can manipulate the result in a yield without blocking.

Here is a bit of code from Translate Bubble:

for {
  textResponse <- clipboardServices.getText(GetTextClipboardRequest())
  persistentResponse <- persistentServices.getLanguages(GetLanguagesRequest())
  translateResponse <- translateServices.translate(
    TranslateRequest(text = textResponse.text, from = persistentResponse.from, to = persistentResponse.to)
  )
} yield (translateResponse.translated)

The methods clipboardServices.getText, persistentServices.getLanguages, and ` translateServices.translate return Future[SomeResponse]. On the left side of <- you see the result contained in the Future and when you are in yield you return the content that you want. As you can see, it's much simpler than using AsyncTask` and at no point you are blocking to wait for the result to return.

Problems during development

Some of the things that I had to change in order to convert it to Scala was:

  • SBT (Scala Build Tools) instead of Gradle. It’s not a big change, especially if you work in Scala, but you’ve got to do the same things using SBT. You can also continue using Gradle with Scala but it´s definetly more limited and you don’t get incremental compilation.
  • Functional Programming instead of Object-Oriented Programming. Scala can combine features from the object-oriented and functional paradigms, but you should learn Functional Programming if you want to take advantage of everything Scala has to offer.
  • Proguard. Scala on Android requires a Proguard run on every build. It’s slow when you compile for the first time, but every time after that it’s quicker. When you have to clean, your project will be slow, but incremental compilation really makes a difference.

Conclusions

We are more productive using Scala and our code is more concise, safer, and 100% Native. We can use all of the advantages of Android and of Scala at the same time. If you want to see the code for Translate Bubble you can check it out on GitHub.

If you’re interested in coding with Scala on Android, we’ll continue to publish new articles on our blog and you can watch me and Raul Raja’s talk about the subject here: Painless Android Development with Scala

As always, if you have any questions or comments, don’t hesitate to reach us on Twitter, or comment on this post.

blog comments powered by Disqus

Ensure the success of your project

47 Degrees can work with you to help manage the risks of technology evolution, develop a team of top-tier engaged developers, improve productivity, lower maintenance cost, increase hardware utilization, and improve product quality; all while using the best technologies.