Java 8 features into Android Development

Email this to someonePrint this pageShare on Google+2Share on Facebook72Tweet about this on TwitterShare on LinkedIn0Share on Reddit0Pin on Pinterest0Share on StumbleUpon3

Java 8 features into Android Development Tools will be demonstrated, specifically aiming at Eclipse IDE. However, steps which will be described throughout this guide might also be adapted to Google’s new uprising development environment, Android Studio. It is based on the community edition of popular IntelliJ Idea IDE by JetBrains and it has recently been upgraded to its ‘beta’ version by Google in early July 2014, slightly before this guide was written. Eclipse will remain as the prominent development environment, at least for a while, and considering the fact that most Android projects have been developed using Eclipse, a method for bringing new Java 8 features like lambda expressions into ADT seems to be quite useful for developers.

Android Development is based on a custom Java implementation called Apache Harmony Project which was terminated back in 2011. The most commonly used Java syntax in Android Development is Java 6 (v1.6) and Java 7 (v1.7) is also partially supported on the KitKat edition (Android 4.4.+). Therefore, Java 8 features like lambda expressions cannot be used directly in the Android App Development without applying some tweaks into the development tools. Luckily, these constructs are basically some ‘syntactic sugar’ enhancements which give developers the shortcomings of things like ‘anonymous classes’ and they can be translated into Java 6 or Java 7 classes.

A recent approach for translating a Java 8 source code into lower Java versions is calledRetroLambda. This library makes developers run Java 8 code with lambda expressions on Java 7 or even lower. Unfortunately, Java 8 features other than lambda expressions are not supported by RetroLambda for now but the concept of lambda expressions is the biggest leap on Java 8 platform and it’s a great tool for Android developers anyway.

Retrolambda
lets you run Java 8 code with lambda expressions and method references on Java 7 or lower. It does this by transforming your Java 8 compiled bytecode so that it can run on a Java 7 runtime. After the transformation they are just a bunch of normal .class files, without any additional runtime dependencies.

Adventurous developers may use Retrolambda to backport lambda expressions even to Java 6 or Java 5. And if you reach Java 5, there are other backporting tools that may let you go down to Java 1.4.Android developers may also use Retrolambda to take advantage of the Java 8 features on Android.Retrolambda can be run as a Maven plugin, Gradle plugin or command line application.

Maven Plugin

To run Retrolambda using Maven, add the following to your pom.xml:

Gradle Plugin

This plugin will automatically build your java or android project with retrolambda, giving you lambda goodness on java 6 or 7. It relies on the wonderful retrolambda by Esko Luontola.

Note: The minimum android gradle plugin is 1.0.0.

Usage

  1. Download jdk8.
  2. Add the following to your build.gradle

The plugin will compile the source code with java8 and then replace the class files with the output of retrolambda.

Configuration

Configuration is entirely optional, the plugin will by default pick up theJAVA6_HOME/JAVA7_HOME/JAVA8_HOME environment variables. It’s also smart enough to figure out what version of java you are running gradle with. For example, if you have java8 set as your default, you only need to define JAVA6_HOME/JAVA7_HOME. If you need to though, you can add a block like the following to configure the plugin:

  • jdk Set the path to the java 8 jdk. The default is found using the environment variableJAVA8_HOME. If you a running gradle with java 6 or 7, you must have either JAVA8_HOME or this property set.
  • oldJdk Sets the path to the java 6 or 7 jdk. The default is found using the environment variableJAVA6_HOME/JAVA7_HOME. If you are running gradle with java 8 and wish to run unit tests, you must have either JAVA6_HOME/JAVA7_HOME or this property set. This is so the tests can be run with the correct java version.
  • javaVersion Set the java version to compile to. The default is 6. Only 6 or 7 are accepted.
  • include 'Debug', 'Release' Sets which sets/variants to run through retrolambda. The default is all of them.
  • exclude 'Test' Sets which sets/variants to not run through retrolambda. Only one of eitherinclude or exclude should be defined.
  • jvmArgs Add additional jvm args when running retrolambda.

Using a Different Version of the retrolambda.jar

The default version of retrolambda used is 'net.orfjackal.retrolambda:retrolambda:1.6.0'. If you want to use a different one, you can configure it in your dependencies.

Android Studio Setup

Add these lines to your build.gradle to inform the IDE of the language level.

Proguard

This plugin is fully compatible with progurad (since v2.4.0). In your progurad file, add

Known Issues

Using Google Play Services causes retrolambda to fail

Version 5.0.77 contains bytecode that is incompatible with retrolambda. To work around this issue, you can either use an earlier version like 4.4.52 or add -noverify to the jvm args.

retrolambda { jvmArgs -noverify }

Compiling for android-L doesn’t work when using Android Studio’s sdk manager.

For some reason only known to the gods, when using Android Studio’s sdk manager, there is noandroid-L directory sdk directory. Instead, it happily builds using the android-20 directory instead. To work around this, you can symlink the android-L directory to point to android-20.

Build fails with using android-apt

This is because android-apt modifies the javaCompile task and this plugin replaces it. Since v2.4.1this is fixed, you just need to ensure you apply this plugin before android-apt.

What Black Magic did you use to get this to work on Android?

There were two hurdles to overcome when compiling for android. The gradle android plugin forces a compile targeting java 6 and uses a custom bootclasspath that doesn’t include necessary java8 files. To overcome this, the plugin:

  1. Overrides -source and -target with 8.
  2. Extracts the necessary files out of the java runtime (rt.jar), and patches android.jar with them.
  3. Sets -bootclasspath to point to the patched android.jar

Command Line Application

Download the latest retrolambda.jar from Maven Central.

Use JDK 8 to compile your source code.

Run Retrolambda, using Java 8, on the class files produced by JDK 8. Run java -jar retrolambda.jar without any additional options to see the instructions (for your convenience they are also shown below).

Your class files should now run on Java 7 or older.

Be sure to run comprehensive tests on your target JVM version (e.g. Java 7), in case the code accidentally uses Java 8 APIs or language features that Retrolambda doesn’t backport.

During development, inside an IDE, it’s the easiest to use Java 8, without Retrolamba, to compile and run tests. But in your continuous integration and release builds you should run all tests using the target Java version. For example, you can configure Maven Surefire Plugin to run tests using a different JVM.

I recommend setting up environment variables JAVA8_HOME, JAVA7_HOME etc. and referring to those variables in the build configuration, instead of relying on what happens to be the default Java version in JAVA_HOME.

You will need Java 8 for compiling and also for generating Javadocs. JDK 7’s Javadoc tool will fail for some valid Java 8 code.

Backported Language Features

Lambda expressions are backported by converting them to anonymous inner classes. This includes the optimization of using a singleton instance for stateless lambda expressions to avoid repeated object allocation.

Method references are basically just syntax sugar for lambda expressions and they are backported in the same way.

Try-with-resources statements are backported by removing calls to Throwable.addSuppressed if the target bytecode version is below Java 7. If you would like the suppressed exceptions to be logged instead of swallowed, please create a feature request and we’ll make it configurable.

Known Limitations

Does not backport Java 8 APIs.

Does not backport Java 8 language features other than lambda expressions.

May break if a future JDK 8 build stops generating a new class for each invokedynamic call. Retrolambda works so that it captures the bytecode that java.lang.invoke.LambdaMetafactorygenerates dynamically, so optimizations to that mechanism may break Retrolambda.

Version History

Upcoming

  • Backports default methods and static methods on interfaces
    • Experimental! Can be enabled with the environment variable DEFAULT_METHODS=2

Retrolambda 1.8.1 (2015-01-06)

  • Backports lambda expressions in an interface’s constant initializer

Retrolambda 1.8.0 (2014-11-16)

  • Backports try-with-resources statements to Java 6 and older by removing calls toThrowable.addSuppressed

Retrolambda 1.7.0 (2014-10-21)

  • Support for serializable lambdas

Retrolambda 1.6.2 (2014-10-03)

  • Fixed a crash when trying to backport Android classes

Retrolambda 1.6.1 (2014-08-25)

  • Fixed a crash when trying backport classes which are nominally the same as those included in the JRE, but which have different bytecode

Retrolambda 1.6.0 (2014-08-20)

  • Does not anymore require the use of a Java agent
  • Maven plugin: by default run Retrolambda in the same process as Maven, making it a bit faster. If Maven is not running under Java 8, then will fall back to forking the process and using the Java agent mechanism

Retrolambda 1.5.0 (2014-07-19)

  • Maven plugin: use the JDK from Maven Toolchains if available. The java8home configuration parameter overrides this

Retrolambda 1.4.0 (2014-07-04)

  • Added an optional -Dretrolambda.includedFiles parameter to support the incremental compilers of build tools
  • Decides which lambda classes to save based on the current class being processed, instead of the class loader that loaded the lambda class

Retrolambda 1.3.0 (2014-06-04)

  • Maven plugin: made the input and output directories configurable
  • Maven plugin: by default use the current JRE for running Retrolambda. For the old behavior, add<java8home>${env.JAVA8_HOME}</java8home> to the plugin configuration

Retrolambda 1.2.3 (2014-05-19)

  • Android: Fixed NoSuchMethodError when calling a private method to which there is a method reference (
  • Fixed the possibility of accidentally overriding private methods to which there is method reference

Retrolambda 1.2.2 (2014-05-15)

  • Fixed method references to private methods; will now make them package-private the same way as lambda implementation methods

Retrolambda 1.2.1 (2014-05-04)

  • Fixed the Retrolambda Maven plugin not using the project’s classpath
  • Maven plugin: save retrolambda.jar under target/retrolambda/
  • Suppress false warning about class initializer methods on interfaces

Retrolambda 1.2.0 (2014-05-02)

  • Maven plugin for running Retrolambda .

Retrolambda 1.1.4 (2014-03-29)

  • Removes from interfaces bridge methods which were generated by JDK 8 e.g. when an interface overrides a method and refines its return type

Retrolambda 1.1.3 (2014-03-25)

  • Fixed incompatibility with the Eclipse JDT compiler, version Kepler SR2 with the Java 8 support patch 1.0.0.v20140317-1959

Retrolambda 1.1.2 (2014-01-08)

  • Updated to work with JDK 8 Early Access Build b121 (2013-12-19)

Retrolambda 1.1.1 (2013-11-27)

  • Show help if the -javaagent parameter is missing

Retrolambda 1.1.0 (2013-07-25)

  • Create only one instance of lambdas which do not capture arguments; i.e. the same optimization as what JDK 8 does
  • Start the sequence number of lambda classes from one (e.g. com.example.Foo$$Lambda$1) for each enclosing class

Retrolambda 1.0.0 (2013-07-23)

  • Backports lambda expressions and method references to Java 7 and older
  • Tested to work with JDK 8 Early Access Build b99 (2013-07-19)

Also, a Gradle plugin for RetroLambda created by another developer allows Gradle-based builds to be implemented in Java or Android Projects. However, the developer only mentions about integrating this plugin into Android Studio environment.

Using these infrastructures within an Eclipse-based development environment cannot be approached directly but it’s doable and will be demonstrated throughout this guide.

Preparation

This guide assumes that the reader has a basic understanding of Android Development and it is based on ADT version 22.6.2 because recent ADT version 23.0.2 seems to have problems like layout folder creation. Details about this issue can be found under the following link:

http://code.google.com/p/android/issues/detail?id=72591

Steps in this guide will be given for a Windows 8.1, 64-bit development machine but they can easily be adapted to other platforms. The new build system Gradle will be used for build/clean processes and its installation procedure will also be provided. Also, both JDK 8 and JDK 7 must coexist on the development machine. Steps given below must be followed to install them:

  • Go to JDK 8 early access preview page http://jdk8.java.net
  • Download JDK 8u20 and install it. JRE 8 installation is not necessary and it can be skipped
  • Go to JDK 7 latest stable release pagehttp://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
  • Download JDK 7u65 and install it. JRE 7 installation is again not necessary and it can be skipped
  • Add JDK 8 home folder and JDK 8 bin folder to your %PATH% variable
  • Create a new environment variable JAVA_HOME with the value of the path of JDK 8 home folder
  • Create a new environment variable JAVA8_HOME again with the value of the path of JDK 8 home folder
  • Create a new environment variable JAVA7_HOME with the value of the path of JDK 7 home folder
  • Open a terminal window and run java -version command and verify that Java 8 is up and running
  • Run javac -version command in the same window and verify that JDK 8 Java compiler is also up and running

Now, ADT-22.6.2 must be downloaded from the following link:

http://dl.google.com/android/adt/22.6.2/adt-bundle-windows-x86_64-20140321.zip

  • Download ADT and unzip its contents into a folder, e.g. D:\adt
  • Define a new environment variable called ANDROID_HOME with the value of the path of your ADT installation folder, e.g. D:\adt\sdk
  • Add your Andoid SDK Platform Tools and Andoid SDK Tools folders, e.g. D:\adt\sdk\toolsand D:\adt\sdk\platform-tools, to your %PATH% variable
  • Create a shortcut to Eclipse IDE if you like. It is located under your ADT installation folder, e.g. D:\adt\eclipse
  • Run Eclipse IDE and create a workspace, e.g. D:\adt\workspace
  • Click on the Android SDK Manager button which is located on the toolbar
  • Select Android SDK Build tools Rev. 19.1 and Android Support Library only. Un-select everything else and install these two packages.

If everything goes well, ADT will be up and running.

The installation of the following tools is also highly recommended:

  • Eclipse Kepler Java 8 Support: It makes Eclipse recognize new Java 8 syntax extensions and makes you get rid of annoying red dots in your Java code editor. It might be installed through Help -> Install New Software in Eclipse. Enterhttp://download.eclipse.org/eclipse/updates/4.3-P-builds/ into the Work with field and continue to install it.
  • Nodeclipse/Enide Gradle: It is mainly used to highlight Groovy language keywords. Groovy is used as the DSL for Gradle build scripts. This plugin can be installed through Eclipse Marketplace. However, Eclipse within ADT-22.6.2 does not come along with Eclipse Marketplace Client. Therefore, you will first need to install Eclipse Marketplace Client by means of Install New Software tool in Eclipse. Enter http//:download.eclipse.org/mpc/kepler/ into the Work with field and continue to install it. After installing Eclipse Marketplace Client, you may search for Nodeclipse/Enide Gradle in the Eclipse Marketplace Client and install it.

Email this to someonePrint this pageShare on Google+2Share on Facebook72Tweet about this on TwitterShare on LinkedIn0Share on Reddit0Pin on Pinterest0Share on StumbleUpon3

About Team

Browse Archived Articles by Team