Use Java 8 on Android! (with a little hacking)

It's Java 8! The Next Best Thing! Lambdas! Static Method Calls! Other stuff that's probably even cooler but I don't care about because it won't be supported on Android for at least three more years!

Huh? Why three? Well, Google deigned to provide us Java 7 support only back in November 2013, with Android Build Tools 19. (Java 8 will officially be supported by Dalvik on the 5th of never, because it's built on a dead Java implementation called Apache Harmony that was discontinued in 2011 and never got past Java 6 compatibility). Maybe when ART gets widespread traction, they'll bake in some of the new Java 8 bytecode, but seeing as they took 3 years to port a few Java 7 features over to the DVM, we're probably looking at the same glacial timespan.

But wait, what about those first two things I mentioned up there? Um, yeah! They work on Android! Look at all that code disappear:

Lambdas!

- .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- pendingVolunteerConfirmed(pickupRequest);
- }
- })
- .setNegativeButton("No", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- cancelPendingVolunteer(pickupRequest);
- }
- })
+ .setPositiveButton("Yes", (dialog, which) -> pendingVolunteerConfirmed(pickupRequest))
+ .setNegativeButton("No", (dialog, which) -> cancelPendingVolunteer(pickupRequest))

Static method references!

- super(context, new ParseQueryAdapter.QueryFactory<PickupRequest>() {
- public ParseQuery create() {
- return PickupRequest.getMyDashboardPickups();
- }
- });
+ super(context, (QueryFactory<PickupRequest>) PickupRequest::getMyDashboardPickups);

"But... how?!" You gurgle. Well...

Here's how, in 3 steps:

1) Install JDK 8.
2) Grab gradle-retrolambda 2.10+ and add this to your top-level build.gradle:

plugins {  
  id "me.tatarka.retrolambda" version "3.1.0"
}

3) Then, add these to your application's build.gradle set the language level:

apply plugin: 'com.android.application'  
apply plugin: 'me.tatarka.retrolambda'

android {  
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

(note: the order of the apply plugin lines matters!)

You're done!

BONUS: Nerdy details about my patch to gradle-retrolambda

Here's my devlog containing some details about my change to gradle-retrolambda that allowed it to auto-discover JAVA_HOME so you don't have to set it yourself.

Let's summarize: in order to run Java 8 on Android, you have to use a bytecode transformer called retrolambda that lets you use lambdas on Java 7. Hence the gradle plugin gradle-retrolambda to make it easy. Then you have to install JDK 8. Then you have to set your JAVA_HOME or JAVA8_HOME env variable because gradle-retrolambda needs it. Yep, manually. Oracle's installer won't set it for you. And you're going to have to set it on every system you build on.

Easy! Okay, yeah, not so much, especially that last part. I hate mucking around with environment variables. That simply just won't do. Let's do some digging.

Finding JAVA HOME without the environment variable

Hmm, Android Studio doesn't seem to need or care about JAVA_HOME. I just point it to the JDK I want to use:
What if I made retrolambda aware of that location somehow? That would be cool, but where's it getting passed to?

Turns out, Android Studio just hands it over via command line by way of the -javaHome parameter when it executes Gradle. How about we grab that? Some more intense Google-fu reveals...

The System.getProperty call! What sort of properties can this divulge?

System.getProperty("java.home")  

Hello!

On my system, that returns C:\Program Files\Java\jdk1.8.0_05\jre, so I'll have to strip out the \jre subfolder. Easy. We have our Java Home!

Patching gradle-retrolambda

Let's hack that into gradle-retrolambda, shall we?

UPDATE: The developer of gradle-retrolambda merged in my pull-request, so you can use Java 8 with it without further fiddling with java_home! Grab gradle-retrolambda 2.10 + and enjoy Java 8 in Android TODAY!

You don't have to wait for the author to accept my pull request, though. And you don't have to patch it yourself. Here's how to use this trick with a stock version retrolambda:

app/build.gradle

supplied_java_home = new java.io.File(System.getProperty("java.home")).getParent()
retrolambda {
    // once my PR is accepted for retrolambda, this whole block can be removed :)     jdk supplied_java_home      javaVersion JavaVersion.VERSION_1_7 }

Enjoy!

http://stackoverflow.com/a/23318644/456568 - the Stack Overflow post that started me on this rabbit hole.