Is your Android code Groovy, baby?

The mobile team at MYOB have been experimenting with Groovy in our codebase. When Apple unveiled Swift at last year's WWDC, I swear I could hear an army of Android developers groan in unison around the world and eventually succumb to what I call Swift Envy™. I decided to start reading the language specifications to find out what the fuss was all about, and not long after I told myself, "I want me some of that!". So I began searching for alternatives to Java on Android, and Groovy eventually came out on top.

Why Groovy?

While the JVM is an excellent virtual machine platform, the Java language itself leaves a lot to be desired. While one could argue that the limited set of functionality and verbosity also limits the extent to which you could screw things up, it does come at the expense of lower signal-to-noise ratio and copious amounts of boilerplate code. The proliferation of design patterns and frameworks is but one evidence of this phenomenon - patterns act as a crutch to get around limitations in the language.

But it's not just about the lack of features in Java, it's also to do with the fact that we live in a highly interconnected world where mobile apps rely on various underlying microservices. And when you have to design mobile software around a microservice architecture, the ease with which you write asynchronous code is key. Java, at least in its Android incarnation, simply doesn't cut it as it's sorely lacking in easy-to-use asynchronous programming idioms. That, coupled with the uncertainty surrounding the future of Java on Android, calls for a reexamination of Java's position on Android.

With that in mind, we went looking for alternatives to Java on Android. Since we inherit a sizeable native Android codebase, we know that the new language has to meet the following criteria

  • It has to coexist with the existing codebase
  • It has to have a low learning curve
  • It offers state-of-the-art asynchronous programming facility
  • It allows us to express more with less code

First, we took a good and hard look at Kotlin, a new kid in the JVM block developed by the good guys at JetBrains. We really liked what we saw, but there are a few issues: the language is too new, it doesn't yet have a proven track record, and the adoption is too low.

Scala is next. I have to admit when I discovered there's a sizeable community of developers building Android software in Scala, it caught me by surprise. Mobile software is all about state and mutable data, but that's anathema to the Scala philosophy of immutability. Nevertheless, Scala doesn't quite meet the criteria as it's not possible to mix Java and Scala in the same codebase with ease. Furthermore, our codebase uses Gradle for its build system while Scala works best with a different beast called SBT.

We then evaluated Java 8 which now has a number of long overdue features like lambdas and streams. While the said functionality is certainly a welcome addition, in our opinion Java 8 is still too verbose and lacks many features that you'd expect in any modern language, not to mention the fact it's not officially supported on Android yet.

Finally, we looked at Groovy. It wasn't long before we fell in love with it. The last time I used it was when it was first introduced in 2003. Back then it was just a little-known esoteric scripting language used by Java developers to automate various things. Twelve years on, boy has it grown. It's now a mature language used by the likes of Google, Netflix, LinkedIn, Oracle, New York Times and Wired, with an active and friendly community to boot.

What is Groovy?

Groovy is a dynamic multi-paradigm language that runs on the Java Virtual Machine (JVM). Did you just say dynamic, on Android? Hold your horses, I'll expand on that in a bit.

While Groovy is mainly object oriented, it also offers plenty of functional programming goodness. Not only does it treat functions as first-class citizens, it also sports other staple functional features such as list operations, immutable data structures, macros and metaprogramming facility, optionals, and modern asynchronous programming idioms.

If you know Ruby, Groovy will look very familiar to you as it borrows a lot of concepts from the former. In fact, the most popular Groovy web framework, Groovy on Grails, is a direct play on Ruby on Rails. Better yet, if you know both Java and Ruby and have to choose between them, Groovy is an excellent middle ground.

For a more refined introduction to Groovy, check out this awesome talk at YOW! 2014.

How does Groovy work on Android?

Thanks to the good folks at Pivotal, Android developers can now, with very little overhead, build mobile and wearable apps in Groovy. They've developed a Gradle plugin that lets you simply pop in an extra classpath and dependency to your Gradle build script and start writing Groovy code in no time.

In case you're curious as to how it works under the hood, check out this excellent blog post by the plugin creator himself. It makes for a very interesting read.

Groovy: the awesome parts

The following section lays down the reasons why we think Groovy is such a good fit for native Android development.

Groovy and Java: a union made in heaven

One of the main reasons we chose Groovy is the fact that it's a superset of Java. Well, kind of. With the exception of a few differences, you can for the most part take an existing .java file, rename it to .groovy, and voila, it's now Groovified! What's more, Groovy allows Java and Groovy code to coexist in harmony in the same codebase through joint compilation. You can even have Java classes that extend or call Groovy ones, and vice versa.

What this means is

  • We don't need to rewrite the whole native Android codebase just to take advantage of Groovy.
  • The learning curve for Java developers is low as they can adopt Groovy constructs at their own pace as and when they become more comfortable with the language.
  • We can choose to stick with Java for existing code and use Groovy for new code where it makes sense, especially in areas where Java falls short.

Closures

While Java 8 signals the advent of lambdas, unfortunately it's not yet available on Android. Groovy on the other hand, has had closures since the beginning. Having closures at your disposal not only allows you to do away with the verbosity (or should I say, monstrosity) of Runnables and anonymous classes, but it also lets you take advantage of the benefits of functional programming like higher-order functions, currying, function composition, trampolining, etc.

The following snippets demonstrate how closures significantly improve the readability of Android code.

Java

new Handler().postDelayed(new Runnable() {
       public void run() {
        dialog.show();
    }
}, 100);

setOnClickListener(new OnClickListener() {
    public void onClick(View view) {
        dialog.show();
      }
});

setOnFocusChangeListener(new OnFocusChangeListener() {
    public void onFocusChange(View view, boolean hasFocus) {
        dialog.show();
    }
});    

Groovy

new Handler().postDelayed({ dialog.show() }, 100)
setOnClickListener { dialog.show() }
setOnFocusChangeListener { dialog.show() }

Enough said.

Improved readability and writeability

Many developers know that code is read more often that it's written, therefore it should be optimized for reading pleasure. But what if you could optimize it for both? With Groovy you can do just that.

If you're a Ruby developer who needs to build an Android app, you'll immediately appreciate what Groovy can do for you. It's much more concise and terse, very reminiscent of Ruby.

  • Groovy does away with getters and setters and replaces them with properties.
  • Semicolons, access modifiers, return types and parentheses are all optional.
  • Inferred typing lets you declare variables with the def keyword without having to specify the actual type. While this may sound trivial or counter-intuitive to some, experience has taught me that it often leads to more readable code.

To get an idea of how concise Groovy is, consider the following code snippets.

Customer.java

public class Customer {
    private String id;
    private String firstName;
    private String lastName;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String firstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String lastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Customer.groovy

class Customer {
    String id
    String firstName
    String lastName
}

List operations

Time for a pop quiz. In your opinion, which of the following pieces of code is more readable?

Java

public int getTaxCodePosition(String uid) {
       int index = 0;
    for (int i = 0; i < getCount(); i++) {
          TaxCode taxCode = getItemAt(i);
          if (guidsMatch(taxCode.getUid(), uid)) {
            index = i;
            break;
          }
    }
    return index;
  }

  private boolean guidsMatch(String leftGuid, String rightGuid) {
       return leftGuid != null && rightGuid != null && leftGuid.equals(rightGuid);
  }

Groovy

int getTaxCodePosition(String uid) {
    items.findIndexOf { it.uid == uid }
  }

If you're like many developers who are used to list operations like map, fold, zip, etc. in other languages, chances are you would prefer the second one. Thanks to Groovy, Android developers can now do away with the pesky loops and conditionals that can make imperative code cumbersome to write and harder to understand.

Better asynchrony

Writing asynchronous and concurrent code is hard. The human brain is simply not wired to think concurrently. That's why a tool that makes it easier to write and verify the correctness of asynchronous code is indispensable.

On Android, asynchronous code is everywhere. There's AsyncTask, Handler, Runnable, callbacks and listeners. The problem with them is you end up writing a lot of boilerplate code. Lots of it. And really, we're talking about code that's strewn all over the place which makes it hard to understand what's going on.

With Groovy, you can use the same set of components available in Java/Android. The key difference is how closures significantly improve the readability of asynchronous code, making it easier to reason about it. Moreover, using something like RxJava is much more pleasant in Groovy since reactive idioms lend themselves more naturally to Groovy syntax. And let's not forget about GPars. Think of it as Task Parallel Library for Groovy, except it also includes more advanced concurrency patterns like actors, STM and CSP.

The following snippet shows how we recently used Groovy to fix a defect in our codebase caused by a race condition.

def waitForContactToLoad() {
    use (TimeCategory) {
        awaitFor({ contact != null }, 10.seconds)
    }
}

def handleShowEmailDialog() {
    waitForContactToLoad().then {
        openEmailDialog()
    }
}

I don't know about you, but I get a warm and fuzzy feeling just looking at that bad boy!

It's dynamic...it's static...it's both!

Groovy has always been, and will always be, a dynamic language. But that doesn't mean you don't get static typing and type checking as well. Confused yet? Read on.

By default the language is dynamic, which means all features of dynamic typing like dynamic dispatch, duck typing, method missing and monkey patching are all available. At the same time, Groovy also provides static compilation and type checking via the @CompileStatic and @TypeChecked annotations.

  • @CompileStatic tells the compiler to statically compile the source code and produce bytecode very similar to Java bytecode. However, this comes at the expense of dynamic dispatch, which means you don't get metaprogramming with it.
  • @TypeChecked is similar to @CompileStatic in that you still get type safety but not static compilation. This means you can take advantage of dynamic dispatch and metaprogramming while still enforcing type-correctness.

Who says you can't have your cake and eat it too?

In our codebase, the ability to mix and match static and dynamic typing proves to be invaluable. One area in which this is very useful is testing. We use Robolectric to unit test our Android codebase. It's awesome, and we love it. However, there are cases where even Robolectric falls short especially when it comes to shadowing certain Android components. Dynamic typing means we can write hard-to-test Android code in a dynamic fashion and leverage Groovy metaclass to easily mock components for which Robolectric provides no shadow classes. As it turns out, this proves to be much more convenient than relying on libraries like Mockito or extending the class under test to manually override its hard-to-mock methods.

Safe navigation operator

public boolean isCompanyFileReadOnly() {
    if (getSession() != null) {
        if (getSession().getCompanyFile() != null) {
            return getSession().getCompanyFile().isReadOnly;
        }
    }
       return false;
  }

Every time I see code like this, part of me dies a slow and painful death. Wouldn't it be better if we could refactor it to use Groovy's safe navigation operator instead?

boolean isCompanyFileReadOnly() {
       session?.companyFile?.readOnly
  }

Ahh, much better.

Beyond mere syntactic sugar, this actually has bigger implications since a majority of the crash reports we receive are caused by NullPointerExceptions. Being able to use the safe navigation operator encourages our developers to write safer and more robust code as doing so is much cheaper than doing nested null checks.

One language to rule them all

Many full-stack developers are accustomed to using best-of-breed languages for various tasks. But if you could choose one language to do everything, would you do it?

Realizing that our build scripts and RoboSpock tests were already written in Groovy, the prospect of using the same language for app code as well proved to be tantalizing. Being able to use the same language means less context switching. Less context switching means we make less mistakes. And less mistakes mean we can focus on delivering value to our customers.

Other Groovy goodness

In addition to the above, there are other awesome Groovy features we've barely scratched the surface of.

And many more! We look forward to taking advantage of these wonderful features to further improve the quality of our Android codebase in the near future.

Groovy baby

What's with the fascination with features and syntax?

At this point, you might be wondering, why the fascination with language features and syntactic sugar? After all, there are other more important decisions a development team have to make besides choosing a tool. But is that really the case?

The answer to that is simple, albeit Socratic - if you could choose a different tool that allows you to write more readable and maintainable code, something more fun to use (or at least something that can alleviate the pain of dealing with a huge codebase) and ultimately increase developer productivity, wouldn't you do it?

Conclusion

Even though Groovy's adoption on Android is far from mainstream, we really think it has a huge potential. And while iOS developers have been gifted the gift of Swift, Android developers now have an equally, if not more, capable tool in Groovy.

With the exception of a few minor kinks (some of which I hope to cover in a future post), our brief but productive stint with Groovy has so far yielded tremendous results. The amount of code we write has gone down, the readability has improved significantly, and most importantly, we're loving our work more. Believe it or not, I actually wake up every morning looking forward to writing more Groovy code!

Cover photo by https://www.flickr.com/photos/maloomy/ under the Creative Commons license