Flutter问题记录 - Unable to find bundled Java version

Posted crasowas

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter问题记录 - Unable to find bundled Java version相关的知识,希望对你有一定的参考价值。

文章目录


前言

有个紧急问题需要修复,本以为很快就能解决继续休假,没想到项目打开运行后android端跑不起来了,ios端正常运行,这就有点莫名其妙,明明放假前还是没问题的,难道我拉取的最新代码有问题?不会吧,谁放假还敲代码啊?🤔️看了下最新的提交记录,还是放假前我提交的,那就肯定不是项目的问题。

开发环境

  • Android Studio: 2022.1.1
  • Flutter: 3.3.10

问题描述

Android端构建运行失败,报错信息如下:

Execution failed for task ':app:processDebugMainManifest'.
> Unable to make field private final java.lang.String java.io.File.path accessible: module java.base does not "opens java.io" to unnamed module

问题分析

先网上搜一搜,大部分都是说JDK版本有问题,提到JDK版本我想到放假最后一天升级了Android Studio版本,从2021.3.1升级到2022.1.1,这应该算大版本更新,难道是这个升级导致JDK版本出问题了。尝试设置项目的JDK版本,折腾一番无果。

既然是Flutter项目,那可以尝试用Flutter命令检查Android Studio正不正常:

flutter doctor

执行输出:

[] Flutter (Channel stable, 3.3.10, on macOS 13.0.1 22A400 darwin-x64, locale
    zh-Hans-CN)
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for
      more details.
[] Xcode - develop for iOS and macOS (Xcode 14.2)
[] Chrome - develop for the web
[!] Android Studio (version 2022.1)
    ✗ Unable to find bundled Java version.
[] IntelliJ IDEA Community Edition (version 2022.3.1)
[] VS Code (version 1.74.2)
[] Connected device (3 available)
[] HTTP Host Availability

原来真有问题,还不止一个,那就逐个解决。

  1. Android license status unknown

这个问题的解决办法Flutter已经给出了,执行下方命令即可:

flutter doctor --android-licenses

执行输出:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/android/prefs/AndroidLocationsProvider has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:650)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:632)

好家伙,这命令执行竟然报错了,我记得老早以前执行过没报错,网上一搜又是JDK版本问题。奇了怪了,虽然我电脑上有装多个版本的JDK,但是Android Studio一直是用的自带的JRE。

  1. Unable to find bundled Java version

这个报错是说找不到捆绑的Java版本🤔️,难道新版的Android Studio移除了自带的JRE?这么一想好像已经找到问题的根源所在了。接下来就是验证这个想法。

首先把Flutter框架项目代码拉下来,搜索关键词Unable to find bundled Java version

关键代码(位于Flutter框架项目下的packages/flutter_tools/lib/src/android/android_studio.dart文件):

final String javaPath = globals.platform.isMacOS ?
    version != null && version.major < 2020 ?
    globals.fs.path.join(directory, 'jre', 'jdk', 'Contents', 'Home') :
    globals.fs.path.join(directory, 'jre', 'Contents', 'Home') :
    globals.fs.path.join(directory, 'jre');
final String javaExecutable = globals.fs.path.join(javaPath, 'bin', 'java');
if (!globals.processManager.canRun(javaExecutable)) 
  _validationMessages.add('Unable to find bundled Java version.');
 else 
// 省略

这段代码的作用就是拼接Java可执行程序的路径,并验证是否能执行。现在我们把涉及到路径的几个变量打印出来,打印很简单,这是Dart写的,直接用print方法就可以了。

print('directory: $directory');
print('javaPath: $javaPath');
print('javaExecutable: $javaExecutable');

现在的问题是怎么执行到修改后的文件,当我们执行flutter doctor命令时,会执行到packages/flutter_tools/bin/flutter_tools.dart文件(后面会分析),然后再进一步解析命令执行。所以我们直接通过Dart命令执行flutter_tools.dart文件是不是就可以了。

dart flutter_tools.dart doctor

切换到Flutter框架项目的packages/flutter_tools/bin目录下执行输出:

flutter_tools.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/executable.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/runner.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/artifacts.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/base/context.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/base/io.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/base/io.dart:28:8: Error: Expected an identifier, but got ';'.
Try inserting an identifier before ';'.
library;
       ^
../lib/src/base/logger.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/base/platform.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^
../lib/src/base/template.dart:1:1: Error: The specified language version is too high. The highest supported language version is 2.18.
// Copyright 2014 The Flutter Authors. All rights reserved.
^

执行报错了,原因是Dart SDK版本低了,电脑上当前的Dart SDK版本是2.18,拉下来的Flutter框架项目代码要求更高的版本。这时候不用去下载最新的Dart SDK版本,Flutter框架项目是自带Dart SDK的,位于项目bin/cache/dart-sdk路径下。

2023/01/26更新:以上说明有点问题,实际的Flutter框架项目默认是不带Dart SDK的,如果你没找到,请看补充说明1

指定Dart可执行程序路径:

flutter框架项目路径/bin/cache/dart-sdk/bin/dart flutter_tools.dart doctor

或者用这个,这个最终也是用到了bin/cache/dart-sdk/bin/dart

flutter框架项目路径/bin/dart flutter_tools.dart doctor

执行输出(省略部分):

directory: /Applications/Android Studio.app/Contents
javaPath: /Applications/Android Studio.app/Contents/jre/Contents/Home
javaExecutable: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java

接下来检查/Applications/Android Studio.app/Contents路径下文件,可以选择[访达] -> [顶部菜单栏的前往] -> [前往文件夹] -> [输入路径跳转]或[应用程序] -> [Android Studio.app] -> [右键显示包内容] -> [Contents]。

实锤了,新版本的Android Studio真的移除了JRE,jre目录找不到,怪不得报错了,不过多了一个jbr目录,找了个以前的Android Studio版本对比(旧版下载,不是很全):

搜了一下jbr(JetBrains Runtime),原来IDEA老早就开始用了,是基于OpenJDK修改的东西。不知道为什么Android Studio从2022.1.1版本才开始支持,去下载了两个预览版也是没有jre目录了,说明后续应该都没了。

综上,问题的根源在于Android Studo移除了jre目录。那如果我直接修改代码,将路径拼接过程中的jre改为jbr是不是就没问题了,实测可行,执行flutter框架项目路径/bin/cache/dart-sdk/bin/dart flutter_tools.dart doctor命令一切正常。

解决方案

如果你略过了前面的问题分析,请注意,以下所说的jbr所在目录路径为:

  • macOS系统:/Applications/Android Studio.app/Contents
  • Windows系统:Android Studio的安装目录

2023/01/30更新:实测以下三种解决方法在Flutter 3.7.0版本依旧适用。对于前两种方法,如果你尝试后未能解决,请先检查上方所说的目录中是否有jre目录或者软链接。

这里提供三种解决方法(任选一种):

  1. 简单粗暴的方法
  • 如果是macOS系统,在jbr同目录下创建一个jre目录,然后将jbr目录内的全部文件复制一份到jre目录下即可。
  • 如果是Windows系统,jre目录是存在的,不过里面几乎没东西,可以直接将jbr目录内的全部文件复制一份到jre目录下即可。
  1. 创建软链接的方法
  • 如果是macOS系统,切换到jbr所在目录执行命令:
ln -s jbr jre

注意,如果是通过cd命令切换到jbr所在目录,需要对路径中的空格进行转义或用单/双引号包裹路径,例如cd /Applications/Android\\ Studio.app/Contents

  • 如果是Windows系统,使用管理员身份打开终端(可以按Win+X键,然后选择Windows 终端(管理员)),切换到jbr所在目录执行命令:
mklink /D jre jbr

这里有几点要注意:

  • 一定要管理员身份运行,不然权限不足会报错提示你没有足够的权限执行此操作
  • 如果打开终端默认用的是PowerShell(输入命令那一行最前面有PS字符),需要输入cmd切换到命令提示符,这是因为PowerShell不支持mklink命令
  • 执行命令前需要删除已经存在的jre目录,不然会报错提示当文件已存在时,无法创建该文件
  • 右键创建快捷方式实测是行不通的,这种方式创建的快捷方式其实是创建了一个新的文件,只不过这个文件属性里面包含了目标路径,查看这个文件大小,才1000字节。通过mklink命令方式创建的,文件大小和占用空间等属性和目标文件属性一致。

2023/02/05更新:通过以上两种方法解决后,Android Studio后续升级可能会遇到如下所示问题:

如果你当前的Flutter版本已经修复了该问题,点击Proceed删除添加的jre目录或者软链接即可。如果还未修复又想继续升级(点击Proceed),请按之前的方法将被删除的jre目录或者软链接添加回去。

  1. 修改Flutter框架代码的方法(不推荐,有点麻烦,感兴趣的可以看看)

前面问题分析中尝试修改代码是可行的,但是那是在Flutter框架项目下改的,现在我们要做的是对当前电脑上的Flutter SDK进行修改。首先找到Flutter SDK目录:

如果你见过Flutter框架项目的目录结构,你会发现一模一样,那是不是说,直接修改目录下的packages/flutter_tools/lib/src/android/android_studio.dart文件就好了,将jre改为jbr,实测没变化。这难道还需要编译?是的,确实还要做一些处理。

flutter doctor命令的执行开始分析,首先找到flutter可执行文件(位于Flutter SDK的bin目录下),这个本质是一个shell脚本,可以用文本工具打开,有点长就贴一个脚本的关键部分:

source "$BIN_DIR/internal/shared.sh"

shared::execute "$@"

在脚本的最后执行了shared::execute::是命名约定,用于分隔库)函数,并将命令执行时的参数传递下去。找到shared.sh文件(位于Flutter SDK的bin/internal目录下)中的shared::execute函数:

# This function is intended to be executed by entrypoints (e.g. `//bin/flutter`
# and `//bin/dart`). PROG_NAME and BIN_DIR should already be set by those
# entrypoints.
function shared::execute() 
  export FLUTTER_ROOT="$(cd "$BIN_DIR/.." ; pwd -P)"

  # If present, run the bootstrap script first
  BOOTSTRAP_PATH="$FLUTTER_ROOT/bin/internal/bootstrap.sh"
  if [ -f "$BOOTSTRAP_PATH" ]; then
    source "$BOOTSTRAP_PATH"
  fi

  FLUTTER_TOOLS_DIR="$FLUTTER_ROOT/packages/flutter_tools"
  SNAPSHOT_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot"
  STAMP_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.stamp"
  SCRIPT_PATH="$FLUTTER_TOOLS_DIR/bin/flutter_tools.dart"
  DART_SDK_PATH="$FLUTTER_ROOT/bin/cache/dart-sdk"

  DART="$DART_SDK_PATH/bin/dart"

  # If running over git-bash, overrides the default UNIX executables with win32
  # executables
  case "$(uname -s)" in
    MINGW*)
      DART="$DART.exe"
      ;;
  esac

  # Test if running as superuser – but don't warn if running within Docker or CI.
  if [[ "$EUID" == "0" && ! -f /.dockerenv && "$CI" != "true" && "$BOT" != "true" && "$CONTINUOUS_INTEGRATION" != "true" ]]; then
    >&2 echo "   Woah! You appear to be trying to run flutter as root."
    >&2 echo "   We strongly recommend running the flutter tool without superuser privileges."
    >&2 echo "  /"
    >&2 echo "📎"
  fi

  # Test if Git is available on the Host
  if ! hash git 2>/dev/null; then
    >&2 echo "Error: Unable to find git in your PATH."
    exit 1
  fi
  # Test if the flutter directory is a git clone (otherwise git rev-parse HEAD
  # would fail)
  if [[ ! -e "$FLUTTER_ROOT/.git" ]]; then
    >&2 echo "Error: The Flutter directory is not a clone of the GitHub project."
    >&2 echo "       The flutter tool requires Git in order to operate properly;"
    >&2 echo "       to install Flutter, see the instructions at:"
    >&2 echo "       https://flutter.dev/get-started"
    exit 1
  fi

  upgrade_flutter 7< "$PROG_NAME"

  BIN_NAME="$(basename "$PROG_NAME")"
  case "$BIN_NAME" in
    flutter*)
      # FLUTTER_TOOL_ARGS aren't quoted below, because it is meant to be
      # considered as separate space-separated args.
      exec "$DART" --disable-dart-dev --packages="$FLUTTER_TOOLS_DIR/.dart_tool/package_config.json" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
      ;;
    dart*)
      exec "$DART" "$@"
      ;;
    *)
      >&2 echo "Error! Executable name $BIN_NAME not recognized!"
      exit 1
      ;;
  esac

函数有点长,直接看最后面就好。最后有个分支语句,根据BIN_NAME的值走不同分支,把相关的变量打印出来看看:

echo "PROG_NAME: $PROG_NAME"
echo "BIN_NAME: $BIN_NAME"

执行flutter doctor输出(省略部分):

PROG_NAME: /usr/local/Caskroom/flutter/3.3.10/flutter/bin/flutter
BIN_NAME: flutter

根据输出结果可以判断,走的是第一个分支,执行exec "$DART" --disable-dart-dev --packages="$FLUTTER_TOOLS_DIR/.dart_tool/package_config.json" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"。之所以会有这个分支判断,是因为Dart命令最终也会执行到这个函数,执行Dart相关命令输出(省略部分):

PROG_NAME: /usr/local/Caskroom/flutter/3.3.10/flutter/bin/dart
BIN_NAME: dart

分析到这,好像也没用到SCRIPT_PATH变量(flutter_tools.dart的路径,请看上述脚本函数代码中的定义),前面问题分析中提到执行flutter doctor命令时,会执行到packages/flutter_tools/bin/flutter_tools.dart文件,这难道是假的?不能说是假的,只能说有点偏差。如果是在这直接执行packages/flutter_tools/bin/flutter_tools.dart文件的,那前面一开始修改代码就可以生效了,原因在于这边是用Dart命令执行SNAPSHOT_PATH所指向的快照文件(flutter_tools.snapshot)去了。

flutter_tools.snapshot是通过Dart命令生成的快照文件,那我们是不是也可以生成一个快照文件替换掉自带的?切换到Flutter SDK目录的packages/flutter_tools/bin路径下执行命令:

dart --snapshot=flutter_tools.snapshot flutter_tools.dart

2023/01/26更新:如果生成快照文件报错,请看补充说明2

然后将生成的flutter_tools.snapshot文件复制到bin/cache目录下,注意,最好先备份原先的flutter_tools.snapshot文件。对比文件大小,发现相差很小,那应该稳了,执行flutter doctor输出:

Doctor summary (to see all details, run flutter doctor -v):
[] Flutter (Channel stable, 3.3.10, on macOS 13.0.1 22A400 darwin-x64, locale
    zh-Hans-CN)
[] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[] Xcode - develop for iOS and macOS (Xcode 14.2)
[] Chrome - develop for the web
[] Android Studio (version 2022.1)
[] IntelliJ IDEA Community Edition (version 2022.3.1)
[] VS Code (version 1.74.2)
[] Connected device (3 available)
[] HTTP Host Availability

• No issues found!

一切正常!🎉

2023/01/26更新:注意,修改Flutter SDK文件后,后续使用Flutter命令升级时可能会提示你是否要保留这些改动,一般来说,使用flutter upgrade --force 命令强制升级即可。

快写完了,才发现差点忘了一开始是因为什么原因写这篇文章的,按以上解决方案做,如果执行flutter doctor命令没问题,实测Android端构建运行也恢复正常了,所以这其实是同一个问题导致的,如果感兴趣的话,可以看看补充分析Flutter问题记录 - Unable to find bundled Java version(续)

补充说明1

如果在Flutter框架项目中没找到Dart SDK,需要先执行命令:

flutter框架项目路径/bin/flutter

执行后,Flutter会自动下载Dart SDK,下载完成后会构建flutter tool,也就是生成flutter_tools.snapshot快照文件。
由于我执行过Flutter命令,所以一开始没注意到项目中默认是不带Dart SDK的。

也许执行命令后你还会遇到这样的问题:

Error: The Flutter directory is not a clone of the GitHub project.
       The flutter tool requires Git in order to operate properly;
       to install Flutter, see the instructions at:
       https://flutter.dev/get-started

解决方法:使用Git工具拉取Flutter框架项目代码,不要直接下载。

补充说明2

如果生成flutter_tools.snapshot快照文件失败,报错提示:

../lib/src/test/flutter_tester_device.dart:180:11: Warning: Operand of null-aware operation '!' has type 'Uri' which excludes null.
 - 'Uri' is from 'dart:core'.
          forwardingUri!,
          ^
../lib/runner.dart:9:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/args-2.3.1/lib/command_runner.dart': No such file or directory
import 'package:args/command_runner.dart';
       ^
../lib/src/runner/flutter_command.dart:6:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/args-2.3.1/lib/command_runner.dart': No such file or directory
import 'package:args/command_runner.dart';
       ^
../lib/runner.dart:10:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/intl-0.17.0/lib/intl.dart': No such file or directory
import 'package:intl/intl.dart' as intl;
       ^
../lib/runner.dart:11:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/intl-0.17.0/lib/intl_standalone.dart': No such file or directory
import 'package:intl/intl_standalone.dart' as intl_standalone;
       ^
../lib/src/runner/flutter_command_runner.dart:6:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/args-2.3.1/lib/command_runner.dart': No such file or directory
import 'package:args/command_runner.dart';
       ^
../lib/src/artifacts.dart:5:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/file-6.1.4/lib/memory.dart': No such file or directory
import 'package:file/memory.dart';
       ^
../lib/src/cache.dart:8:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/file-6.1.4/lib/memory.dart': No such file or directory
import 'package:file/memory.dart';
       ^
../lib/src/artifacts.dart:6:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/process-4.2.4/lib/process.dart': No such file or directory
import 'package:process/process.dart';
       ^
../lib/src/cache.dart:10:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/process-4.2.4/lib/process.dart': No such file or directory
import 'package:process/process.dart';
       ^
../lib/src/commands/analyze.dart:6:8: Error: Error when reading '../../../.pub-cache/hosted/pub.flutter-io.cn/process-4.2.4/lib/process.dart': No such file or directory
import 'package:process/process.dart';
       ^

执行命令获取flutter_tools所需要的依赖包即可解决:

flutter update-packages

总结

写完这篇文章,想着不知道有没有人提issue,要是没人提,我去提一个。没想到,老早就有人提了issue,应该是用Android Studio预览版的人提的,最近也还有人不断在重复提。

最后

如果这篇文章对你有所帮助,请不要吝啬你的点赞👍加星🌟,谢谢~

以上是关于Flutter问题记录 - Unable to find bundled Java version的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Unable to ‘pub upgrade‘ flutter tool

Flutter环境配置问题Unable to find bundled Java version.

Flutter 开发环境搭建Unable to ‘pub upgrade‘ flutter tool. Retrying in five seconds...

Flutter 开发环境搭建Unable to ‘pub upgrade‘ flutter tool. Retrying in five seconds...

Flutter, Android Unable to load Maven meta-data from ~ Build error 解决方法

Flutter小记2Android加载图片资源出现Unable to load asset的解决方案