The right Java bytecode version for Android

Android up to API level 23 does not support all possibilities of Java 8, the classes that the compiler generates have a version flag to denote which bytecode extensions can be inside of it. If you ever happen to hit an error like this during your build

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.RuntimeException: Exception parsing classes
        at com.android.dx.command.dexer.Main.processClass(Main.java:752)
        at com.android.dx.command.dexer.Main.processFileBytes(Main.java:718)
        at com.android.dx.command.dexer.Main.access$1200(Main.java:85)
        at com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645)
        at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
        at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
        at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
        at com.android.dx.command.dexer.Main.processOne(Main.java:672)
        at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574)
        at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)
        at com.android.dx.command.dexer.Main.run(Main.java:277)
        at com.android.dx.command.dexer.Main.main(Main.java:245)
        at com.android.dx.command.Main.main(Main.java:106)
Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
        at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
        at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
        at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
        at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
        at com.android.dx.command.dexer.Main.parseClass(Main.java:764)
        at com.android.dx.command.dexer.Main.access$1500(Main.java:85)
        at com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684)
        at com.android.dx.command.dexer.Main.processClass(Main.java:749)
        ... 12 more
1 error; aborting

:app:transformClassesWithDexForDebug FAILED

you most likely included a class generated with a compiler target-level that was too advanced ( 34 hexadecimal in the case above is the level 52 of the Java 8 class format).
Android as of today supports all features of the JDK 7 compiler (see e.g. the release notes of the ADT plugin http://developer.android.com/tools/sdk/eclipse-adt.html).

In order to actually find the classes that were compiled against the wrong Java version you can use this command and check for anything that is >= 52

javap -v *.class | grep major 
# it'll report e.g.:
  major version: 50

To define which Java bytecode compatibility level to use and still have the latest jdk8 in your JAVA_HOME, you can use this in the build section of your Maven pom:

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>

 

The Gradle Java plugin is configured like this (the compatibility definition needs to be after applying the java plugin to take effect and gradle-android projects have this stored in the compileOptions instead as shown below)

allprojects {
    apply plugin: 'java'
    sourceCompatibility = 1.7
    targetCompatibility = 1.7
}

// Gradle-Android projects have this stored in the compileOptions instead:
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    // [..]
}

Source and target compatibility are usually set to the same level, you need to be careful and should know what you are doing if you set them to different values, for example linkage errors at runtime may occur if the classes refer to newer java apis that were not present earlier.