AndroidStudio3 支持 Java8 了请问你敢用吗

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

GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
如果你想靠AI翻身,你先需要一个靠谱的工具!

摘要: 今天早上,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可识别的语法。
说的太抽象具体表现我们看代码。

1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  new Thread(() -> {
    Log.d("kymjs", "========");
  }).start();
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)的操作,下面看一个非纯函数的。
编译前:

1
2
3
4
5
6
7
8
9
10
11
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();
  }
}

编译后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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编译前代码:

1
2
3
4
5
6
7
8
9
10
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部分,方法内依旧是普通对象方法调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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编译前代码:

1
2
3
4
5
6
7
8
9
10
11
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编译后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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对象了:

1
2
3
4
5
6
7
8
9
10
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 支持的噱头。

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://my.oschina.net/kymjs/blog/1558015

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • Android原生项目集成Flutter解决方案

    Android原生项目集成Flutter解决方案

    这篇文章主要介绍了Android原生项目集成Flutter解决方案,想了解Flutter的同学可以参考下
    2021-04-04
  • Android多种支付方式的实现示例

    Android多种支付方式的实现示例

    App的支付流程,添加多种支付方式,不同的支付方式,对应的操作不一样,有的会跳转到一个新的webview,有的会调用系统浏览器,有的会进去一个新的表单页面,等等,本文就给大家详细介绍一下Android 多种支付方式的优雅实现,需要的朋友可以参考下
    2023-09-09
  • Android PopupWindow使用实例

    Android PopupWindow使用实例

    这篇文章主要介绍了Android PopupWindow使用实例,本文直接给出代码实例,需要的朋友可以参考下
    2015-06-06
  • 功能强大的登录界面Android实现代码

    功能强大的登录界面Android实现代码

    这篇文章主要为大家分享了功能强大的登录界面Android实现代码,验证码制作方法,自带一键删除功能,用户名密码为空时抖动提示效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Android制作微信添加多个图片放大图片功能

    Android制作微信添加多个图片放大图片功能

    这篇文章主要介绍了Android制作微信添加多个图片放大图片功能,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-03-03
  • Android仿淘宝商品详情页效果

    Android仿淘宝商品详情页效果

    这篇文章主要为大家详细介绍了Android仿淘宝商品详情页效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • Android RecyclerView的简单使用

    Android RecyclerView的简单使用

    这篇文章主要为大家详细介绍了Android RecyclerView简单使用的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03
  • android 实现在照片上绘制涂鸦的方法

    android 实现在照片上绘制涂鸦的方法

    今天小编就为大家分享一篇android 实现在照片上绘制涂鸦的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • Android编程滑动效果之倒影效果实现方法(附demo源码下载)

    Android编程滑动效果之倒影效果实现方法(附demo源码下载)

    这篇文章主要介绍了Android编程滑动效果之倒影效果实现方法,基于继承BaseAdapter自定义Gallery和ImageAdapter实现倒影的功能,并附带demo源码供读者下载参考,需要的朋友可以参考下
    2016-02-02
  • Android利用RecyclerView实现列表倒计时效果

    Android利用RecyclerView实现列表倒计时效果

    这篇文章主要为大家详细介绍了Android利用RecyclerView实现列表倒计时效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09

最新评论