AndroidStudio3 支持 Java8 了请问你敢用吗

 更新时间:2017年11月09日 10:43:57   投稿:mrr  
Google 发布了 AS 3.0,以及一系列的 Support 包,有意思的新东西挺多,AS3里面有一个亮眼的特性就是支持J8。接下来通过本文给大家分享AndroidStudio3 支持 Java8 的相关内容,感兴趣的朋友一起看看吧

摘要: 今天早上,Google 发布了 AS 3.0,以及一系列的 Support 包,有意思的新东西挺多,因为之前一直在看 kotlin的支持,特地翻了一下对 Java8 的支持方式,结果……

本文开源实验室原创,转载请以链接注明:https://kymjs.com/code/2017/10/26/01/

支持 Java 8

kotlin 相关的东西很早以前我就在讲了,这里就不再细说了。AS3里面有一个亮眼的特性就是支持J8。首先说一下为什么以前我们不能用Java8的新特性,最主要的原因就是 lambda 语法。在 JVM 中,Java8 的语法是通过一个叫做invokedynamic的字节码操作命令完成的,但是这东西在 dalvik 中并没有,因此一直不能用。

现在AS3.0之所以能用,实际上是在新的Android Studio中加入了一个desugar的东西,他就类似JVM上的invokedynamic,把Java8的字节码翻译成dalvik可识别的。

官网介绍:

Android Studio provides built-in support for using certain Java 8 language features and third-party libraries that use them. As shown in figure 1, the default toolchain implements the new language features by performing bytecode transformations, called desugar, on the output of the javac compiler.

desugar 能干啥

首先看张官方图:

desugar

在 javac 执行后,desugar 会对 class 做操作,将内部的lambda相关的语法转换为dalvik可识别的语法。
说的太抽象具体表现我们看代码。

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  new Thread(() -> {
    Log.d("kymjs", "========");
  }).start();
}

一个这样的Java8 lambda语法的代码被编译以后,反编译它,可以看到变成了这样:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  this.setContentView(2131296283);
  (new Thread(MainActivity$$Lambda$0.$instance)).start();
}
// $FF: synthetic class
final class MainActivity$$Lambda$0 implements Runnable {
  static final Runnable $instance = new MainActivity$$Lambda$0();
  private MainActivity$$Lambda$0() {
  }
  public void run() {
    MainActivity.lambda$onCreate$0$MainActivity();
  }
}

我们看到上面的代码,在编译后的run()方法内有一句MainActivity.lambda$onCreate$0$MainActivity();其实这一句就是原本lambda body,他被转换成了 MainActivity 类中的一个 static method。在最终编译成 dex 后会再次优化,减少一次方法调用直接变成run方法的body(相当于内联)。具体原理操作请见源码的visitInvokeDynamicInsn方法:GoogleCode请自备梯子

上面的代码演示了纯函数(什么是纯函数自己wiki)的操作,下面看一个非纯函数的。
编译前:

public class MainActivity extends AppCompatActivity {
  String mString = "hello";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(() -> {
      Log.d("kymjs", "========" + mString);
    }).start();
  }
}

编译后:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  this.setContentView(2131296283);
  (new Thread(new MainActivity$$Lambda$0(this))).start();
}
// $FF: synthetic class
final class MainActivity$$Lambda$0 implements Runnable {
  private final MainActivity arg$1;
  MainActivity$$Lambda$0(MainActivity var1) {
    this.arg$1 = var1;
  }
  public void run() {
    this.arg$1.lambda$onCreate$0$MainActivity();
  }
}

原本的 lambda 静态对象不再是静态的了;lambda 类的构造方法多了一个外部类对象的引用。因此,如果 lambda body 不是一个非纯函数,是有可能会造成内存泄漏的(原因跟内部类持有外部类对象是一样)。
最后:附一篇官方介绍:https://developer.android.com/studio/write/java8-support.html

方法引用

这个就实在是让我哭笑不得了。官网标注,Method References 完全支持了,原本想到kotlin 的高阶函数会有性能问题,还想看看Java8会不会有这个问题。但是我用了一下,槽点满满。不管是Supplier还是Predicate,Function所有的方法调用都得要最低 API24,我靠现在普遍都是兼容到14的吧,你这让我怎么用高阶方法。不过我也尝试不考虑低版本写了一个,看了一下效果。

首先是Java8编译前代码:

public String str = "hello";
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  test(() -> str);
}
public void test(Supplier<String> block) {
  System.out.println("=======" + block.get());
}

java8编译后,貌似换汤不换药,只替换lambda部分,方法内依旧是普通对象方法调用:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  this.setContentView(2131296283);
  this.test(new MainActivity$$Lambda$0(this));
}
public void test(Supplier<String> block) {
  System.out.println("==========" + (String)block.get());
}
// $FF: synthetic class
final class MainActivity$$Lambda$0 implements Supplier {
  private final MainActivity arg$1;
  MainActivity$$Lambda$0(MainActivity var1) {
    this.arg$1 = var1;
  }
  public Object get() {
    return this.arg$1.lambda$onCreate$0$MainActivity();
  }
}

kotlin编译前代码:

val str: String = "hello"
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
  test {
    str
  }
}
fun test(block: () -> String) {
  println("=========${block.invoke()}")
}

kotlin编译后的代码:

protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView((int) R.layout.activity_main);
  test(new MainActivity$onCreate$1(this));
}
public final void test(@NotNull Function0<String> block) {
  Intrinsics.checkParameterIsNotNull(block, "block");
  System.out.println("=========" + ((String) block.invoke()));
}
/* compiled from: MainActivity.kt */
final class MainActivity$onCreate$1 extends Lambda implements Function0<String> {
  final /* synthetic */ MainActivity this$0;
  MainActivity$onCreate$1(MainActivity mainActivity) {
    this.this$0 = mainActivity;
    super(0);
  }
  @NotNull
  public final String invoke() {
    return this.this$0.getStr();
  }
}

所以说,kotlin在实现上跟Java也依旧是一模一样,首先生成一个类,把lambda转换成对象,再调用这个对象的invoke()方法。但是别忘了,kotlin有神奇的inline关键字,就专门用来解决这种莫名其妙的多生成一大堆对象的情况。
假设给上面的test()方法加上inline关键字后,编译后的代码就变成了这样,相当于并没有调用test()方法,因此也就不存在多生成的Function0对象了:

protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView((int) R.layout.activity_main);
  MainActivity this_$iv = this;
  System.out.println("=========" + this.str);
}
public final void test(@NotNull Function0<String> block) {
  Intrinsics.checkParameterIsNotNull(block, "block");
  System.out.println("=========" + ((String) block.invoke()));
}

原本我是想夸一夸J8的支持,因为我最初以为他是将 lambda body 的纯函数转换成静态方法,直接将 lambda 改成静态方法调用来做的,结果没想到还不如 kotlin。感觉就是 Google 为了 KPI 去加了一个 Java8 支持的噱头。

相关文章

  • Android ProgressBar 模拟进度条效果的实现

    Android ProgressBar 模拟进度条效果的实现

    这篇文章主要介绍了Android ProgressBar 模拟进度条效果的实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Android Studio 3.6 layout文件text模式切换问题

    Android Studio 3.6 layout文件text模式切换问题

    这篇文章主要介绍了Android Studio 3.6 layout文件text模式切换问,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • android开发设计模式之——单例模式详解

    android开发设计模式之——单例模式详解

    本篇文章主要介绍了android开发设计模式之——单例模式详解,具有一定的参考价值,有需要的可以了解一下。
    2016-11-11
  • Android JNI处理图片实现黑白滤镜的方法

    Android JNI处理图片实现黑白滤镜的方法

    这篇文章主要介绍了Android JNI处理图片实现黑白滤镜的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Android自定义View制作仪表盘界面

    Android自定义View制作仪表盘界面

    这篇文章主要介绍了Android自定义View制作仪表盘界面的相关资料,首先需要自定义仪表盘的属性,在构造方法种获取自定义属性,本文介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • Android开发使用自定义view实现ListView下拉的视差特效功能

    Android开发使用自定义view实现ListView下拉的视差特效功能

    这篇文章主要介绍了Android开发使用自定义view实现ListView下拉的视差特效功能,结合实例形式详细分析了Android重写ListView控件实现ListView下拉视差效果的步骤与相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Android+Html5混合开发仿微信朋友圈

    Android+Html5混合开发仿微信朋友圈

    这篇文章主要为大家详细介绍了Android+Html5混合开发仿微信朋友圈的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • 详解Android数据存储之SQLCipher数据库加密

    详解Android数据存储之SQLCipher数据库加密

    对于已经ROOT的手机来说的没有任何安全性可以,一旦被利用将会导致数据库数据的泄漏,本篇文章主要介绍了Android数据存储之SQLCipher数据库加密,具有一定的参考价值,有需要的可以了解一下。
    2016-12-12
  • Android中删除Preference详解

    Android中删除Preference详解

    这篇文章主要介绍了Android中删除Preference详解,很多时候删除Preference总会失败,本文着重分析删除失败的原因,需要的朋友可以参考下
    2015-01-01
  • Android手机管理工具类详解

    Android手机管理工具类详解

    这篇文章主要为大家详细介绍了Android手机管理工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08

最新评论