Java 8 for Android

hello world

在android上采用java的main方法编译并运行

//main.java
class Java {
  public static void main(String... args) {
    System.out.println("hello world");
  }
}

使用本地的jdk编译代码生成class文件。

TIMFENG-MB0:android fengguangtu$ ls
main.java
TIMFENG-MB0:android fengguangtu$ javac main.java 
TIMFENG-MB0:android fengguangtu$ ls
Java.class main.java
TIMFENG-MB0:android fengguangtu$ java Java
hello world

使用android sdk的dx或者d8工具把class文件转成dex文件。

TIMFENG-MB0:android fengguangtu$ /Users/fengguangtu/Library/Android/sdk/build-tools/28.0.3/d8 --release Java.class 
TIMFENG-MB0:android fengguangtu$ ls
Java.class  classes.dex main.java

将dex文件push到手机并执行。

TIMFENG-MB0:android fengguangtu$ adb push classes.dex /sdcard
classes.dex: 1 file pushed. 0.0 MB/s (792 bytes in 0.119s)
TIMFENG-MB0:android fengguangtu$ adb shell dalvikvm -cp /sdcard/classes.dex Java
hello world

以上的方法只能使用标准的Java 类,不能使用android相关的类比如log。 在执行时会报jni方法找不到。

java.lang.UnsatisfiedLinkError: println_native
    at android.util.Log.println_native(Native Method)
    at android.util.Log.i(Log.java:159)
    at org.slf4j.impl.AndroidLogger.info(AndroidLogger.java:151)
    at org.gihon.client.TunnelingClient.<init>(TunnelingClient.java:62)
    at org.gihon.client.CLI.main(CLI.java:95)
    at dalvik.system.NativeStart.main(Native Method)

native 方法实际位于libandroid_runtime.so 该so并不能直接加载。 在android M版本之前可以使用com.android.internal.util.WithFramework

dalvikvm -cp /some/path/classes.dex com.android.internal.util.WithFramework Java "This is an argument"

在android M版本之后可以使用app_process 来加载java的类。

#!/system/bin/sh
# Copied by example from am command
base=/system
export CLASSPATH=/sdcard/classes.dex
exec app_process $base/bin Java "$@"

java的新特性可以从实现上来说可以分为两种:编译时特性和运行时特性。

android从最开始的java6经历了java7和java8新特性的支持,针对这些新的特性逐一讨论下。

java7

java7的特性,Android Studio

Try-with-resources statement

BufferedReader br = new BufferedReader(new FileReader(path));
try {
   return br.readLine();
} finally {
   br.close();
}

to

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}

这个特性既可以用编译时特性实现,也可以用运行时特性。 作为运行时特性的话在编译时不用任何特殊处理,但在运行时需要API level >= 19. 作为编译时特性,可以使用java8模式编译代码,使用该模式后,对运行平台没有限制。该模式的实现原理在后面有详细介绍

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

Improved Type Inference for Generic Instance Creation(Diamond Operator)

Map<String, List<String>> anagrams = new HashMap<String, List<String>>();

to

Map<String, List<String>> anagrams = new HashMap<>();

这个特性指在使用模板时可以省略右表达式的类型。 这个问题我们的sdk在编译时有遇到相应的报错。原因是AS编辑器默认是支持java7的,但build.bat脚本编译的时候是按java6编译的。理论上现在是可以把编译脚本升级为java7的。

    <target name="compile" depends="init,set_buildtime">
        <echo message="complie......" />
        <javac fork="yes" encoding="UTF-8" debug="false" includeantruntime="true" source="1.6" target="1.6" srcdir="${project.src}" destdir="${build.out.classes}" bootclasspath="${build.android.jar}">
            
            <exclude name="tmsdk/common/roach/nest/**"/>
            <classpath>
                <fileset dir="${project.libs}" includes="*.jar" />
            </classpath>
        </javac>        
    </target>

Strings in switch

String s = ...
switch(s) {
 case "quux":
    processQuux(s);
    // fall-through

  case "foo":
  case "bar":
    processFooOrBar(s);
    break;

  case "baz":
     processBaz(s);
    // fall-through

  default:
    processDefault(s);
    break;
}

Binary literals

int binary = 0b1001_1001;

Multiple exception catching

} catch (FirstException ex) {
     logger.error(ex);
     throw ex;
} catch (SecondException ex) {
     logger.error(ex);
     throw ex;
}

to

} catch (FirstException | SecondException ex) {
     logger.error(ex);
    throw ex;
}

SafeVarargs

@SuppressWarnings({"unchecked", "varargs"})
public static void printAll(List<String>... lists){
    for(List<String> list : lists){
        System.out.println(list);
    }
}

to

@SafeVarargs
public static void printAll(List<String>... lists){
    for(List<String> list : lists){
        System.out.println(list);
    }
}

java8

先说下java8引入的一比较常用的特性,详细的支持列表可以参见官方网站.

Lambda

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        doSomething();
    }
});

to

button.setOnClickListener(view -> doSomething());

代码减少了模版代码,没有访问限定词,没有返回值,也没有方法名。

// A functional interface has exactly one method
public interface OnClickListener {
    void onClick(View view);
}

lambda表达式由三部分组成

button.setOnClickListener((view) -> {doSomething()});
  1. 在()中声明函数的所有参数
  2. ->
  3. 在{}放函数的具体实现 ()和{}是可以省略的

方法引用

button.setOnClickListener(view -> doSomething(view));
button.setOnClickListener(this::doSomething);

// functional interface
public interface OnClickListener {
    void onClick(View view);
}

// referenced method: must take View as argument, because onClick() does
private void doSomething(View view) {
    // do something here
}
method reference example
static methods MyClass::doSomething
instance method of object myObject::doSomething
constructor MyClass:: new
instance method of any argument type String::compare

Default interface Methods

public interface MyView {
    void showProgressbar();
    default void hideProgressbar() {
        // do something here
    }
}
Java 8 language feature Compatible minSdkVersion
Lambda expressions Any. Note that Android does not support the serialization of lambda expressions.
Method references Any.
Type annotations Any. However, type annotation information is available at compile time, but not at runtime. Also, the platform supports TYPE in API level 24 and below, but not ElementType.TYPEUSE or ElementType.TYPEPARAMETER.
Default and static interface methods Any.
Repeating annotations Any.
Java 8 Language API Compatible minSdkVersion
java.lang.annotation.Repeatable API level 24 or higher.
AnnotatedElement.getAnnotationsByType(Class) API level 24 or higher.
java.util.stream API level 24 or higher.
java.lang.FunctionalInterface API level 24 or higher.
java.lang.reflect.Method.isDefault() API level 24 or higher.
java.util.function API level 24 or higher.

java8特性的实现原理

android build process

Android Studio 通过对jdk编译生成的class文件进行 bytecode 转换, 在转换过程中使用desugar程序对java8的特性代码转化为全平台支持的字节码,上述特性中,支持所有android平台的特性都是通过该方式实现的。

如何开启java8特性

gradle android插件的版本在3.0及以上版本。 或者修改build.gradle

android {
  ...
  // Configure only for each module that uses Java 8
  // language features (either in its source code or
  // through dependencies).
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

参考:

  1. Use Java 8 language features
  2. Embracing Java 8 language features
  3. Android's Java 8 Support
  4. New features in java 7
  5. Android-Java8-Sample