Android gradle 中为什么要添加 java8 兼容配置?

2,156 阅读6分钟

太长不看版

当你的 Android gradle plugin 版本低于 4.2时(后面简称为AGP)。Android Studio(后简称AS) 默认使用 java7 作为语法检查。

当你的代码,或者你依赖的代码中存在 java8 种的语法,比如 lambda。 并且你没有添加 java8 兼容配置。AS在语法分析,或者编译的时候,都会报错。

lambda expressions are not supported in -source 7 

为了解决这个报错,可以通过三种方式解决。

  1. 代码中消除 java8 的lambda。(很难做到)
  2. 添加 java8 的兼容配置 (容易做到)
  3. 升级 AGP 到 4.2 (中等难度)

所以,为了解决这个错误,选择方式 2 是较为容易的。

遇到了问题

项目是一个混合 Flutter 的项目,使用到了 flutter shared_preferences。 在4.15日打包失败,错误提示为:

.../flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_android-2.1.1/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java:84: error: static interface methods are not supported in -source 7  
static [@nonnull](https://github.com/nonnull) MessageCodec getCodec() {  
^  
(use -source 8 or higher to enable static interface methods)  
.../flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_android-2.1.1/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java:103: error: lambda expressions are not supported in -source 7  
(message, reply) -> {  
^  
(use -source 8 or higher to enable lambda expressions)  
2 errors

检查 shared_preferences 依赖为 ^2.10.0。 最近一周没有改过这个依赖,并且最近打包都是正常的。

在搜索引擎中搜索 -source 8 or higher to enable lambda expressions 提示应该在项目中添加

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

但是主项目已经添加过这个代码块了,难道要改 shared_preferences?

查找解决方案

查找 flutter shared_preferences github 。发现有这个 issue

[(github.com/flutter/flu…)]

有人回复可以通过如下方式解决

image.png

dependency_overrides:
  shared_preferences_android: 2.1.0
  shared_preferences_ios: 2.1.0

可以看出是写死了 flutter 依赖的版本号。

flutter^2.10.0代表取 2.10.x 的最高版本。去掉 ^,就是写死版本号。

继续看这个 issue ,后面有人发了另外一个解决方式

image.png

对应的 pr[github.com/flutter/pac…]

对应的代码片段如下:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8 
    targetCompatibility JavaVersion.VERSION_1_8
 }

确实是在 shared_preferencesbuild.gradle 中添加的,也解决了问题,不再报错。

思考: 为什么可以这样解决

问问 chatgpt 下面这段代码是什么意思?

: Android 中下面这段代码是什么意思

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8 
    targetCompatibility JavaVersion.VERSION_1_8
}

:

这段代码是在 Android build.gradle 文件中的配置选项,它指定了 Java 代码编译时所使用的源码和目标代码兼容性版本都为 Java 8。

- `sourceCompatibility JavaVersion.VERSION_1_8` 表示源码兼容性版本为 Java 8。
- `targetCompatibility JavaVersion.VERSION_1_8` 表示目标代码兼容性版本为 Java 8。

这意味着你可以在你的代码中使用 Java 8 的新语言特性和 API,并且编译后的 APK 可以在支持 Java 8 的设备上运行。

反问自己:理解了吗?

自问自答:没有。

尝试:如果在我的项目里去掉 sourceCompatibility JavaVersion.VERSION_1_8 会怎么样?

IDE 直接摆烂

image.png

猜测: Android Studio 默认使用 Java 7 用于代码检查,以及编译吗?

验证猜测

前置准备

1: 需要用的命令

javap -v file

通过这个命令,可以得到 .class 文件对应的java编译版本

2: Android项目中编译出来的 .class 文件在哪里?

Android项目中,java文件的编译产物目录为:

app/build/intermediates/javac/debug/compileDebugJavaWithJavac

如果你发现这个目录下是空的,试试下面几个办法

  • 确定你的已经 rebuild 成功
  • 删除 build 目录,并且 rebuild 项目
  • 如果你没有 java 代码,都是 kotlin 代码,那么这个目录肯定是空的。写一个 java 类吧。
验证场景一: 删除 java_1.8 兼容性代码, 运行命令 javap -v MainActivity.java

得到以下信息

image.png

上图中的 major version: 51 表示这个文件是由 java7 编译的。

验证场景二: 保留 java_1.8 兼容性代码, 运行命令 javap -v MainActivity.java

得到以下信息

image.png

上图中 major version: 52 表示这个文件是由 java 8 编译的。

初步结论:Android Studio 默认用 java7 编译代码。

如果需要 java8 的语法,比如 lambda,需要额外加上

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8 
    targetCompatibility JavaVersion.VERSION_1_8
}

上面的结论是有误的,下面继续说为什么错误。

再思考:是谁决定了这个编译版本的?

思考一:项目怎么用 java7 来编译了?

Android Studio 中,JDK 的版本号已经是 java8 或者是 java11。 我绝对不会指定 java7来编译项目的。

image.png

思考二:sp的开发者有那么不专业吗?

shared_preferences_android 项目,提交 2.1.11版本的那位开发同学。 具体提交 commit [github.com/flutter/pac…]

他写这些代码的时候,IDE直接也报错了吧,还能提交吗?不可能这么不专业吧。

因此我猜测有其他工具,来确定项目使用什么版本的 java 来编译。

猜测一: 是 gradle 决定项目的java编译版本吗?

验证猜测:直接问 chatgpt

问:android gradle 会使用什么java版本来编译android项目?

答:

根据官方文档,Android Gradle插件默认使用Java 8版本来编译Android项目。然而,从Android Gradle插件3.0版本开始,还支持使用Java 7或更高版本来编译Android项目。

开发者可以在build.gradle文件中的android {}代码块中通过设置compileOptions {}来指定使用的Java版本。例如,设置为Java 7:


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

注意,要在项目中使用Java 8的新特性(例如Lambda表达式),需要在应用模块的build.gradle文件中添加以下代码来启用Java 8语言功能:

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

  // For Java 8 language support
  jackOptions {
    enabled true
  }
}


但是从Android Gradle插件3.0版本开始,官方已经弃用Jack编译器,建议使用Java 8语言功能时,使用D8或R8编译器。

chatgpt得到的答案为 Android Gradle Plugin ,简称 AGP,决定了Android项目的java编译版本。

那么 AGP 版本和 Java编译版本的关系是什么呢?

浏览了 AGP 发布的日志 developer.android.google.cn/build/relea…

image.png

发现 AGP 从 4.2 版本开始,默认使用 java8 来编译代码,并且 AS 以 java8 作为检查基准,就是图片中的 language level

可以得到结论: 当你的 AGP 版本高于或等于 4.2 ,你不需要添加

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

也可以使用 java8 语法糖,编译器会默认使用 java8 来编译你的代码。

小结:项目的编译版本默认是 AGP 版本决定的。或者你可以添加兼容性代码来决定编译版本。

回答上面的猜疑二:

shared_preferences_android 项目,提交 2.1.11版本的那位开发同学。 具体提交 commit 他写这些代码的时候,IDE直接也报错了吧,还能提交吗?不可能这么不专业吧。

查看 shared_preferences_android 2.11.1agp 版本

github.com/flutter/pac…

image.png

AGP 7.2.2 默认使用 java8 来编译项目。 所以提交 shared_preferences_android lambda 代码的开发者,项目不会报错。

因此可以得到如下小结:项目中 AGP 版本为 4.1.2,默认使用 java7 编译项目, 第三方依赖的 AGP 即便设置到高版本,也会使用 主项目的低版本 AGP

如果第三方依赖要使用 java8 lambda,必须添加兼容配置。

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }