Skip to content

[Feature]: 简化 Java 检测逻辑,优先使用 DropOut 管理的 Java #80

@BegoniaHe

Description

@BegoniaHe

前置确认

  • 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息
  • 我未仔细阅读这些内容,只是一键已读所有内容,并相信这不会影响问题的处理
  • 我已搜索现有 Issue,确认这不是重复的功能请求
  • 我已查看 README 中的 Roadmap
  • 这个功能将使多个用户受益,而不仅仅是我自己

功能类型

Java 管理

问题陈述

当前的 Java 检测逻辑(src-tauri/src/core/java.rs 中的 get_java_candidates() 函数)存在以下问题:

现存问题

  1. 代码冗长且难以维护

    • 652-812 行(160+ 行)包含大量硬编码路径和平台特定逻辑
    • 每个平台都有独立的路径扫描代码块
    • 维护成本高,添加新路径需要修改多处
  2. 检测范围过大

    • 遍历系统中所有可能的 Java 安装路径:
      • Linux: /usr/lib/jvm, /usr/java, /opt/java, /opt/jdk, /opt/openjdk, SDKMAN 路径等
      • macOS: /Library/Java/JavaVirtualMachines, Homebrew 路径等
      • Windows: C:\Program Files\Java, Eclipse Adoptium, AdoptOpenJDK, Microsoft JDK, Zulu, Corretto, LibericaJDK 等
  3. 性能问题

    • 即使找到合适的 Java 也会继续扫描所有路径
    • 大量文件系统 I/O 操作拖慢启动速度
  4. 与 DropOut 的 Java 下载机制理念冲突

    • 我们已经提供了完整的 Java 自动下载功能
    • 为何还要如此复杂地检测系统 Java?
    • 这会导致用户困惑:"为什么我下载了 Java 还检测不到?"

当前架构问题

// 当前逻辑(652-812 行)
fn get_java_candidates() -> Vec<PathBuf> {
    let mut candidates = Vec::new();
    
    // 检查 PATH
    if let Ok(output) = Command::new("where/which").arg("java").output() { ... }
    
    #[cfg(target_os = "linux")]
    {
        // Linux: 50+ 行硬编码路径
        for base in ["/usr/lib/jvm", "/usr/java", "/opt/java", ...] { ... }
    }
    
    #[cfg(target_os = "macos")]
    {
        // macOS: 40+ 行硬编码路径
        for path in ["/Library/Java/...", "/opt/homebrew/...", ...] { ... }
    }
    
    #[cfg(target_os = "windows")]
    {
        // Windows: 60+ 行硬编码路径
        for base in ["C:\\Program Files\\Java", ...] { ... }
    }
    
    // 检查 JAVA_HOME(重复检查)
    if let Ok(java_home) = std::env::var("JAVA_HOME") { ... }
    
    candidates
}

考虑到 DropOut 已经提供了:

  • 内置 Adoptium Java 下载功能
  • 版本管理和缓存机制
  • 可靠的 Java 版本兼容性保证
  • 按需下载,节省磁盘空间

过度依赖系统 Java 检测反而带来了不必要的复杂性和潜在 bug。

建议的解决方案

采用 最小化系统 Java 检测策略,优先使用 DropOut 管理的 Java。

新的检测优先级顺序

1. DropOut 下载的 Java(app_data_dir/java/*)  ← 优先级最高,完全可控
   ↓ 如果没有找到合适版本
2. JAVA_HOME 环境变量                        ← 用户显式配置,应当尊重
   ↓ 如果仍未找到
3. 提示用户通过 DropOut 下载 Java             ← 引导用户使用内置功能

代码简化方案

重构后的核心函数:

/// 检测所有可用的 Java 安装(优先 DropOut 管理的 Java)
pub fn detect_all_java_installations(app_handle: &AppHandle) -> Vec<JavaInstallation> {
    let mut installations = Vec::new();
    
    // 第一层:DropOut 下载的 Java(优先级最高)
    installations.extend(detect_dropout_java(app_handle));
    
    // 第二层:JAVA_HOME 环境变量(用户显式配置)
    if let Ok(java_home) = std::env::var("JAVA_HOME") {
        let java_bin = find_java_executable(&PathBuf::from(&java_home));
        if let Some(path) = java_bin {
            if let Some(java) = check_java_installation(&path) {
                if !installations.iter().any(|j| j.path == java.path) {
                    installations.push(java);
                }
            }
        }
    }
    
    // 去重并排序(DropOut Java 优先,然后按版本降序)
    installations.sort_by(|a, b| {
        let is_dropout_a = a.path.contains("dropout") || a.path.contains("temurin");
        let is_dropout_b = b.path.contains("dropout") || b.path.contains("temurin");
        
        match (is_dropout_a, is_dropout_b) {
            (true, false) => std::cmp::Ordering::Less,   // DropOut Java 优先
            (false, true) => std::cmp::Ordering::Greater,
            _ => parse_java_version(&b.version).cmp(&parse_java_version(&a.version)),
        }
    });
    
    installations
}

/// 检测 DropOut 下载的 Java 版本
fn detect_dropout_java(app_handle: &AppHandle) -> Vec<JavaInstallation> {
    let mut installations = Vec::new();
    let dropout_java_dir = get_java_install_dir(app_handle);
    
    if !dropout_java_dir.exists() {
        return installations;
    }
    
    if let Ok(entries) = std::fs::read_dir(&dropout_java_dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            if path.is_dir() {
                if let Some(java_bin) = find_java_executable(&path) {
                    if let Some(java) = check_java_installation(&java_bin) {
                        installations.push(java);
                    }
                }
            }
        }
    }
    
    installations
}

移除的代码

  • get_java_candidates() 的 160 行平台特定路径扫描(652-812 行)
  • detect_java_installations() 的复杂逻辑(626-650 行)
  • 所有硬编码的系统路径:
    • Linux: /usr/lib/jvm, /usr/java, /opt/java, SDKMAN 等
    • macOS: /Library/Java, Homebrew, Cellar 等
    • Windows: Program Files\Java, Eclipse Adoptium, AdoptOpenJDK 等

保留的功能

  • JAVA_HOME 环境变量检测(用户显式配置)
  • DropOut 下载的 Java 管理
  • Java 版本兼容性检查(get_compatible_java, is_java_compatible
  • Java 自动下载功能(download_and_install_java

替代方案

如果必须保留系统 Java 检测,使用现成的 crate 而不是自己维护 160 行代码:

[dependencies]
java-locator = "0.1.9"
// 使用 java-locator
if let Ok(java_home) = java_locator::locate_java_home() {
    // 使用检测到的 Java
}

示例和参考

其他启动器的做法

  1. Prism Launcher 9.0+

    • 提供"Auto-download Mojang Java"选项
    • 优先使用启动器下载的 Java
    • 系统 Java 检测是可选功能
  2. ATLauncher

    • 默认使用内置 Java
    • 仅在高级设置中允许指定系统 Java
    • 不主动扫描系统路径
  3. GDLauncher

    • 完全使用自己管理的 Java
    • 不检测系统安装
    • 提供清晰的 Java 版本选择界面

优先级

中(锦上添花)

贡献意愿

  • 我愿意实现这个功能
  • 我愿意帮助测试这个功能
  • 我可以提供设计草图或规范

其他信息

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions