Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2636a89
refactor: 将SharedPreferences中的java字段改为javaSelectedPath,添加javaList缓存字段…
futurw4v May 2, 2026
dcf4cad
refactor(java.dart): 重命名_loadJavaRuntimesFromPrefs为_initJavaRuntimes,…
futurw4v May 2, 2026
f28db70
refactor: 将java_manager.dart重命名为java_utils.dart,新建java_service.dart,将…
futurw4v May 2, 2026
78f0a02
refactor: 完善SharedPreferences的数据写入,优化部分逻辑
futurw4v May 2, 2026
ac467c4
feat: 使JavaService不阻塞应用加载
futurw4v May 2, 2026
32783d5
feat: 更改main.dart中检测java是否存在的逻辑
futurw4v May 2, 2026
a933db7
refactor(java_service.dart): 将javaList改为javaRuntimes并使其存储包含完整JavaRunt…
futurw4v May 3, 2026
4a84de7
chore: 添加win32_registry插件,更新插件
futurw4v May 3, 2026
60d0051
feat(java_utils.dart): 添加在windows下搜索注册表的逻辑,优化代码
futurw4v May 3, 2026
62f5646
fix(java_service.dart): 修复遗漏了_javaRuntimes的添加
futurw4v May 3, 2026
31b1b4f
feat(java.dart): 添加手动添加Java的逻辑
futurw4v May 3, 2026
192ebb6
feat: 重命名currentJavaPath变量为javaSelectedPath
futurw4v May 3, 2026
413231e
feat(java.dart): 添加长按操作逻辑
futurw4v May 3, 2026
137170f
feat(java.dart): 添加刷新逻辑,修正部分错误
futurw4v May 3, 2026
29d6711
feat(java.dart): 将刷新java的逻辑与UI隔离使UI更流畅
futurw4v May 3, 2026
2992d5b
chore(fabric.dart): 在if语句下面添加大括号
futurw4v May 3, 2026
8471d2c
fix(java_utils.dart): 确保注册表操作被正确关闭
futurw4v May 9, 2026
35d5ba9
fix(java.dart): 修复Row布局冲突
futurw4v May 29, 2026
7dc2a24
refactor(java_service.dart): 整理方法,重命名等
futurw4v May 29, 2026
216298e
fix(java.dart): 修复刷新时子线程数据无法同步
futurw4v May 29, 2026
20d4752
feat(java.dart): 在搜索完成后添加SnackBar提示
futurw4v May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
| PathList | 游戏文件夹列表 | list(string) |
| Path_$name | 版本路径 | string |
| Game_$name | 版本列表 | list(string) |
| java | Java路径 | string |
| javaSelectedPath | 所选Java路径 | string |
| javaRuntimes | Java运行时列表(以JSON格式存储) | string |
Comment thread
futurw4v marked this conversation as resolved.

离线账号配置 offline_account_$name list(string)
| 序号 | 值 |
Expand Down
147 changes: 147 additions & 0 deletions lib/function/java/java_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import 'dart:convert';
import 'dart:io';

import 'package:fml/function/java/java_utils.dart';
import 'package:fml/function/java/models/java_info.dart';
import 'package:fml/function/java/models/java_runtime.dart';
import 'package:shared_preferences/shared_preferences.dart';

class JavaService {
JavaService._();

static List<JavaRuntime> _javaRuntimes = [];
static List<JavaRuntime> get javaRuntimes => _javaRuntimes;

static String _javaSelectedPath = '';
static String get javaSelectedPath => _javaSelectedPath;

static JavaInfo? _systemDefaultJavaInfo;
static JavaInfo? get systemDefaultJavaInfo => _systemDefaultJavaInfo;

static late final Future<void> initFuture;

static Future<void> init() async {
final futures = await Future.wait([
JavaUtils.getSystemDefaultJavaInfo(),
SharedPreferences.getInstance(),
]);

_systemDefaultJavaInfo = futures[0] as JavaInfo?;
SharedPreferences prefs = futures[1] as SharedPreferences;

final cachedJson = prefs.getString('javaRuntimes') ?? '';
final systemJavaInfo = _systemDefaultJavaInfo;
final List<String> validPaths = [];

_javaRuntimes = [];

late final List<JavaRuntime> cachedRuntimes;

if (cachedJson.isEmpty) {
// 初次打开/缓存为空,直接执行搜索并写入
_javaRuntimes = await JavaUtils.searchPotentialJavaExecutables();
updateJavaRuntimes(_javaRuntimes, prefs);
} else {
cachedRuntimes = await readJavaRuntimesFromPrefs(cachedJson);

// 遍历缓存的列表
for (final javaRuntime in cachedRuntimes) {
// 仅检测对应文件是否存在
if (await File(javaRuntime.executable).exists()) {
_javaRuntimes.add(javaRuntime);
validPaths.add(javaRuntime.executable);
}
}
}
Comment on lines +44 to +55
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

如果 SharedPreferences 中缓存的 javaRuntimes JSON 字符串损坏或格式不兼容,readJavaRuntimesFromPrefs 会抛出异常,导致整个 init() 初始化流程崩溃。\n建议使用 try-catch 包裹缓存解析逻辑,在解析失败时记录日志并清除损坏的缓存,然后自动触发重新搜索,以提高应用的健壮性。

Suggested change
} else {
cachedRuntimes = await readJavaRuntimesFromPrefs(cachedJson);
// 遍历缓存的列表
for (final javaRuntime in cachedRuntimes) {
// 仅检测对应文件是否存在
if (await File(javaRuntime.executable).exists()) {
_javaRuntimes.add(javaRuntime);
validPaths.add(javaRuntime.executable);
}
}
}
} else {
List<JavaRuntime> cachedRuntimes = [];
try {
cachedRuntimes = await readJavaRuntimesFromPrefs(cachedJson);
} catch (e) {
LogUtil.log('解析缓存的 Java 运行时失败:$e', level: 'WARN');
}
// 遍历缓存的列表
for (final javaRuntime in cachedRuntimes) {
// 仅检测对应文件是否存在
if (await File(javaRuntime.executable).exists()) {
_javaRuntimes.add(javaRuntime);
validPaths.add(javaRuntime.executable);
}
}
}


// 缓存内java列表出现了变化,再次写入SharedPreferences
if (cachedJson.isNotEmpty && validPaths.length != cachedRuntimes.length) {
updateJavaRuntimes(_javaRuntimes, prefs);
}

// 缓存内路径全部失效,搜索Java
if (_javaRuntimes.isEmpty) {
_javaRuntimes = await JavaUtils.searchPotentialJavaExecutables();

updateJavaRuntimes(_javaRuntimes, prefs);
}

/// 处理_currentJavaPath
_javaSelectedPath = prefs.getString('javaSelectedPath') ?? '';

// 若不存在/为空,设置为系统java,若系统java也不存在,设置为扫描到的第一个JavaRuntime
if (_javaSelectedPath.isEmpty) {
if (systemJavaInfo != null) {
_javaSelectedPath = systemJavaInfo.path;
} else if (javaRuntimes.isNotEmpty) {
_javaSelectedPath = _javaRuntimes.first.executable;
}

prefs.setString('javaSelectedPath', _javaSelectedPath);
} else {
// 缓存的java已无效,重复上方逻辑
final info = await JavaUtils.probeJavaExecutable(_javaSelectedPath);

if (info == null) {
if (systemJavaInfo != null) {
_javaSelectedPath = systemJavaInfo.path;
} else if (javaRuntimes.isNotEmpty) {
_javaSelectedPath = _javaRuntimes.first.executable;
}

prefs.setString('javaSelectedPath', _javaSelectedPath);
}
Comment on lines +73 to +93
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

当系统默认 Java 存在但其路径为空(例如 java -version 成功但 where/which 查找失败)时,systemJavaInfo.path 将会是空字符串 ""。\n此时如果 _javaSelectedPath 为空,代码会直接将 _javaSelectedPath 设为 systemJavaInfo.path(即 ""),并写入 SharedPreferences。这会导致启动游戏时因可执行文件路径为空而报错。\n建议在检查 systemJavaInfo 时,同时确保其 path 不为空。

    if (_javaSelectedPath.isEmpty) {
      if (systemJavaInfo != null && systemJavaInfo.path.isNotEmpty) {
        _javaSelectedPath = systemJavaInfo.path;
      } else if (javaRuntimes.isNotEmpty) {
        _javaSelectedPath = _javaRuntimes.first.executable;
      }

      prefs.setString('javaSelectedPath', _javaSelectedPath);
    } else {
      // 缓存的java已无效,重复上方逻辑
      final info = await JavaUtils.probeJavaExecutable(_javaSelectedPath);

      if (info == null) {
        if (systemJavaInfo != null && systemJavaInfo.path.isNotEmpty) {
          _javaSelectedPath = systemJavaInfo.path;
        } else if (javaRuntimes.isNotEmpty) {
          _javaSelectedPath = _javaRuntimes.first.executable;
        }

        prefs.setString('javaSelectedPath', _javaSelectedPath);
      }
    }

}
}

///
/// 从JSON字符串反序列化出存储的JavaRuntimes列表
///
static Future<List<JavaRuntime>> readJavaRuntimesFromPrefs(
String input,
) async {
final List<dynamic> decoded = jsonDecode(input);

return decoded
.map((e) => JavaRuntime.fromJson(e as Map<String, dynamic>))
.toList();
}

///
/// 更新[_javaRuntimes]并写入
///
static Future<void> updateJavaRuntimes(
List<JavaRuntime> newList, [
SharedPreferences? prefsIn,
]) async {
_javaRuntimes = newList;

// 如果外部传了prefs就用外部的,否则内部获取实例
final prefs = prefsIn ?? await SharedPreferences.getInstance();

final String jsonString = jsonEncode(newList);

await prefs.setString('javaRuntimes', jsonString);
}

///
/// 更新[_javaSelectedPath]并写入
///
static Future<void> updateJavaSelectedPath(String newPath) async {
// 检查路径
if (newPath == _javaSelectedPath) return;

final newFile = File(newPath);

if (!newFile.existsSync()) {
throw ArgumentError('路径不存在: $newPath');
}

// 更新路径并写入
_javaSelectedPath = newPath;

final prefs = await SharedPreferences.getInstance();

await prefs.setString('javaSelectedPath', _javaSelectedPath);
}
}
Loading