AO(sp)y Episode 2 - Lambdas

This post is the second episode in a series where I peek into random commits on AOSP and try to predict what’s coming to the world of Android development.

Disclaimer: These tidbits have been gleaned primarily from the commit messages on AOSP. I very rarely even read (let alone understand) the code. As such expect inaccuracies or downright mistakes here. Do not make decisions based on what you read in this post.

That out of the way, let’s dig in. This episode was supposed to be about toolchain improvements, but the headline feature is what the title says - lambdas! Lest I am accused of clickbait, let me clarify that all of this is still experimental. Several commits are not even merged. There is no saying when (and if) any of these features will actually find their way into production.

Ever since Java8 was released, the clamor for using lambdas in Android development has been growing, with good reason too. Android is full of anonymous inner classes for all kinds of things and several of these use cases would benefit from the use of lambdas.

Java 8 brought about two types of enhancements to the Java programming language:

  1. Features and syntax in the language itself
  2. Additions to the standard libraries to take advantage of the new features

This article concentrates purely on the first type of enhancements, i.e.,

It does not look like there is any move to bring the enhancements to the runtime libraries in the form of new collections and java.util.function package to Android.

So, what’s cooking?

Runtime support

Opcodes for creating and invoking lambdas are being added to the Dalvik bytecode (dex) format. The ART interpreter is being updated to be able to use these opcodes.

The first of these commits also points to the dex2oat compiler being taught how to compile lambdas from your dex bytecode into the corresponding opcodes for OAT.

What does all this mean? It means that lambdas might be supported natively in a future version of Android.

Toolchain enhancements

This commit and related ones point to the possibility of Jack compiler doing basically what is achieved with gradle-retrolambda today. In other words, Jack compiler will be able to compile lambdas into bytecode that works even on runtimes that don’t support lambda opcodes. Similarly, Jill will likely do to lambdas in .class/.jar libraries what Jack does to them in source code.

Other Java8 features

There’s more to Java8 than just the lambda syntax. AOSP also has commits that hint at support for default methods and Single Abstract Methods. I did not find any obvious mention of method references, but that might be because I’m not looking hard enough. In any case, method references are just syntax sugar for lambdas so if lambda expressions are supported, so should method references.

Compatibility

At first glance, one might conclude that lambdas will be used only if minSdkVersion >= the version of Android in which they are introduced, and that Jack’s feature of down-compiling lambdas into Java7-compatible bytecode will be used in all other cases. This means that the runtime lambdas will not be used even on the runtimes that support them unless minSdkVersion is set to the latest. However, this is (more than) likely to negatively impact the adoption of lambdas.

So, what might be strategies to work around this? What follows is a wild guess as to how this might pan out. I haven’t dug in the source code into much (or … any) detail (not that I’d understand much of it even if I had, but that’s beside the point).

Remember that dex2oat runs on the Android device. The APK file only has dex bytecode, and it is converted into OAT at installation time. Could this be used to enhance the compatibility story?

Jack might use targetSdkVersion to decide whether to include lambdas in the compiled dex bytecode. If targetSdkVersion >= the version in which lambda support is introduced, then the generated dex might include the Java8 bytecode. But wait, aren’t we back at square one? dex2oat is part of the Android OS and cannot (yet) be updated independently of the OS. This means that dex2oat that is running on an older version of the OS will not understand the new dex bytecode.

This is why I say include Java8 opcodes instead of generate - that is to say, Jack might generate both the Java7-compatible byte code as well as new Java8 opcodes. Hopefully, dex2oat running on older versions of Android simply ignore the Java8 opcodes. But, what about the size implications of this arrangement? In lambda-heavy apps, will this result in bloated .dex files?

The other option is that Jack might always generate Java7 compatible bytecode (compiles lambda expressions into anonymous inner classes), but it might include some sort of flag to indicate to dex2oat that these constructs be compiled into the corresponding Java8 OAT format. But this begs the question - what, then, is the point of the new opcodes in .dex format at all?

I do not have the answers to these questions yet (nor does AOSP). But I’m sure the answers will reveal themselves in the days to come. If you are interested in this topic, you can follow the progress yourself at the following locations on AOSP: