AndroidStudio3 支持 Java8 了,就问你敢用吗

本文深入探讨了AS3如何通过desugar技术支持Java8的lambda语法和方法引用,解释了lambda表达式在编译过程中的转换机制,以及可能引起的内存泄漏问题。同时,对比了Java与Kotlin在实现上的差异。

本文开源实验室原创,转载请以链接注明: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 支持的噱头。

转载于:https://my.oschina.net/kymjs/blog/1558015

<think>首先,用户的题是基于之前的引用和上下文。用户提供了几个引用,这些引用讨论的是Android Studio对Java 8支持题。用户意图是确认最新版Android Studio是否支持Java 8,并可能希望找到解决方法或确认使用Java 8的可行性。 关键点: - 引用[1]提到:支持Java 8需要Jack编译器,但Jack仅在Android Studio 2.1及更高版本上受支持。 - 引用[2]:用户遇到错误"Extension methods are not supported at this language level",并建议在build.gradle中添加Java 8依赖。 - 引用[3]和[4]:报告了其他错误,如进程退出非零或找不到方法。 用户的题是:"请最新版本的 Android Studio 是否支持 Java 8,如果不支持该如何配置或解决?" 我需要回答: 1. 确认最新版Android Studio是否支持Java 8。 2. 如果不支持,提供配置或解决方法。 3. 基于系统指令:使用中文回答,确保LaTeX语法正确(但这个题可能不需要数学表达式),结构清晰,生成相关题。 背景知识: - Android Studio的最新版本是Android Studio Flamingo或更高(截至2023年),但用户引用提到Android Studio 2.1,这很旧。Jack编译器已被弃用。 - 现代Android Studio(从3.0开始)通过D8和R8编译器支持Java 8特性,无需Jack。 - 标准法是在build.gradle中添加Java 8兼容性选项。 结构化回答: 1. **引言**:直接回答用户题。 2. **支持情况**:解释最新版Android Studio支持Java 83. **配置方法**:提供步骤如何配置。 4. **常见题**:如果用户遇到错误,建议解决方法。 5. **生成相关题**:结束时添加相关题。 参考引用: - 用户提供的引用是旧信息(如引用[1]提到Jack,但Jack已弃用)。我需要更新信息。 - 在回答中,如果需要,可以引用这些来源,但应澄清它们过时。 回答内容: - 最新版Android Studio(如Arctic Fox或更高)支持Java 8特性。 - 配置方法:在app的build.gradle文件中添加: ```gradle android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } ``` - 如果遇到错误,检查Gradle版本、JDK版本等。 - 生成相关题:例如,关于其他Java版本支持、优化等。 确保系统指令: - 行内数学表达式:可能不需要,但如果有,用$...$。 - 独立公式:可能不需要。 - LaTeX语法正确:在代码块中没题。 - 中文回答。 - 生成相关题:在末尾添加。 - 引用的段落末尾添加引用标识:用户提供的引用,我可以引用它们,但需说明它们过时。 最终回答结构: 1. 确认支持。 2. 提供配置步骤。 3. 解释可能的错误和解决方法。 4. 添加相关题。</think>### 最新版 Android Studio 对 Java 8支持与配置方法 最新版本的 Android Studio(如 **Android Studio Flamingo (2022.3.1)** 或更高版本)**完全支持 Java 8 特性**,无需依赖旧版 Jack 编译器。早期版本(如 Android Studio 2.1)曾通过 Jack 支持 Java 8,但 Jack 已被弃用多年[^1]。现代 Android Studio 使用 **D8/R8 编译器** 和 **Gradle 插件** 原生兼容 Java 8,包括 Lambda 表达式、方法引用等特性。以下是详细说明和配置方法: #### 1. **支持情况说明** - **最新版支持**:从 Android Studio 3.0(2017 年发布)开始,Google 移除了 Jack 编译器,改为通过标准工具链支持 Java 8[^1]。当前版本(如 Android Studio Hedgehog 或 Iguana)默认集成 JDK 11+,但可无缝编译 Java 8 代码。 - **关键变化**:早期错误(如引用[2]中的 "Extension methods are not supported" 或引用[4]的 "Could not find method jackOptions()")通常源于过时的配置或 Gradle 版本过低。现代项目中,**Jack 已完全废弃**,无需额外启用[^1][^4]。 #### 2. **配置 Java 8 支持** 在项目中启用 Java 8 特性,只需在 `app/build.gradle` 文件中添加 **compileOptions** 配置。以下是标准步骤: ```gradle android { compileSdkVersion 34 // 推荐使用最新 SDK 版本 // 添加 Java 8 兼容性选项 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } ``` - **作用**: - `sourceCompatibility`:指定源代码使用 Java 8 语法。 - `targetCompatibility`:确保编译后的字节码兼容 Java 8 运行时。 - **验证**:同步 Gradle 后,即可使用 Lambda 表达式、Stream API 等特性,例如: ```java button.setOnClickListener(v -> System.out.println("Java 8 Lambda 已启用")); ``` #### 3. **常见错误及解决方法** 如果遇到题(如用户引用中的错误),请检查以下方面: - **错误 1:语言级别不支持**(如引用[2]) **原因**:未正确配置 `compileOptions` 或 JDK 版本不匹配。 **解决**: 1. 确认 `build.gradle` 中已添加上述配置。 2. 在 Android Studio 中设置 JDK: - 菜单栏 → File → Project Structure → SDK Location → 选择 JDK 1.8 路径。 - **错误 2:Gradle 进程失败**(如引用[3]) **原因**:Gradle 版本过旧或依赖冲突。 **解决**: 1. 升级 Gradle:在 `gradle-wrapper.properties` 中设置最新版(如 `distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip`)。 2. 清理缓存:运行 `./gradlew cleanBuildCache`。 - **错误 3:JackOptions 相关报错**(如引用[4]) **原因**:项目残留了废弃的 Jack 配置。 **解决**: 1. 删除 `build.gradle` 中所有 `jackOptions { ... }` 代码块。 2. 确保未启用实验性功能(如 `android.enableJetifier=false`)。 #### 4. **最佳实践建议** - **JDK 选择**:推荐使用 **JDK 17**(Android Studio 内置),但编译目标仍可设为 Java 8 以兼容旧设备。 - **Gradle 插件**:使用 **Android Gradle Plugin 8.1.0+**(最新版为 `8.3.0`),避免兼容性题。 - **测试验证**:新建一个 Lambda 表达式测试类,确认无编译错误。 > **注意**:用户提供的引用[1]-[4]反映了早期版本(Android Studio 2.x 时代)的题,但截至 2023 年后,所有维护版本均已解决这些限制[^1][^2][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值