1. 适用场景与背景

在本地使用命令行进行 Android 工业级应用或包含 C/C++ JNI 原生模块(如 MuPDF、WebRTC、OpenCV 等)的工程构建时,常因系统历史残留或不同开发工具(Android Studio、VS Code、命令行 sdkmanager)分别配置了不同的环境变量,导致构建系统拒绝执行或因 NDK 损坏中断。


2. 核心陷阱与症状 (Pitfall & Symptoms)

2.1 Gradle 8.14+ 多 SDK 路径冲突直接拒编

  • 报错信息:

    FAILURE: Build failed with an exception.
    * What went wrong:
    Several environment variables and/or system properties contain different paths to the SDK:
      ANDROID_HOME = C:\Users\<user>\Android\Sdk
      ANDROID_SDK_ROOT = C:\Users\<user>\AppData\Local\Android\Sdk
    Please set them to the same path or delete one of them.
  • 触发机理:从 Android Gradle Plugin (AGP) 8.x 与 Gradle 8.14 起,构建工具对环境变量的一致性校验更加严苛。当检测到 ANDROID_HOME 与 ANDROID_SDK_ROOT 指向不同文件系统目录时,Gradle 视其为不可预测的构建输入歧义,直接以 Exception 形式拒编。

2.2 NDK source.properties 缺失导致原生编译失败

  • 报错信息:

    NDK at C:\Users\<user>\AppData\Local\Android\Sdk\ndk\<version> did not have a source.properties file
  • 触发机理:虽然某一 SDK 目录完整,但如果构建进程默认落入另一个残留或不完整的 SDK 目录,其内部缺少完整的 NDK 描述文件,会导致 CMake/ndk-build 解析失败。

3. 根因分析 (Root Cause Analysis)

  1. 历史变量交替:

    • 早年 Google 官方曾宣布废弃 ANDROID_HOME,推荐使用 ANDROID_SDK_ROOT;
    • 随后在现代 Gradle 工具链中,又重新建议统一定位至 ANDROID_HOME;
    • 开发者机器上安装过多个版本的 Android Studio(如 Hedgehog、Iguana、Ladybug)或不同命令行脚本时,容易在 Windows 用户级或系统级环境变量中遗留不同路径。
  2. 全局环境修改的副作用:

    • 若直接在自动化构建脚本中调用 setx 修改 Windows 用户或系统注册表,会引发其他并行 IDE 窗口、终端或其他 Android 项目的环境状态漂移与不可逆损坏。

4. 解决方案与最佳实践 (Solution & Best Practice)

4.1 子进程级别的环境变量隔离 (Process-Level Isolation)

构建脚本应坚持 “只在当前子进程内清理与重定向,绝不污染系统全局变量” 原则。

Linux / macOS / Git Bash

# 临时消除 ANDROID_SDK_ROOT,保留经过验证的 ANDROID_HOME
env -u ANDROID_SDK_ROOT ./gradlew :app:assembleDebug

Windows PowerShell

在自动化脚本或 CI 脚本开头动态检测并清除:

# 1. 显式清除当前进程的 ANDROID_SDK_ROOT
$env:ANDROID_SDK_ROOT = $null

# 2. 动态探测具备有效 NDK (source.properties) 的 SDK 根目录
$Candidates = @(
    "C:\Users\$env:USERNAME\Android\Sdk",
    "C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"
)

$ValidSdk = $null
foreach ($path in $Candidates) {
    if (Test-Path $path) {
        $ndkPath = Join-Path $path "ndk"
        if (Test-Path $ndkPath) {
            $hasValidNdk = Get-ChildItem -Path $ndkPath -Recurse -Filter "source.properties" -ErrorAction SilentlyContinue | Select-Object -First 1
            if ($hasValidNdk) {
                $ValidSdk = $path
                break
            }
        }
    }
}

if ($ValidSdk) {
    $env:ANDROID_HOME = $ValidSdk
}

# 3. 执行 Gradle 构建命令
.\gradlew :app:assembleDebug

4.2 统一环境的根治方案 (Permanent Fix)

在日常个人开发机中,若需彻底根除该歧义:

  1. 打开 Windows 系统属性 -> 环境变量;
  2. 彻底删除用户变量及系统变量中的 ANDROID_SDK_ROOT;
  3. 仅保留 ANDROID_HOME,将其值统一定位到包含最新 SDK、Build-Tools 及完整 NDK 的绝对路径(如 C:\Users\<user>\Android\Sdk);
  4. 重启终端使变量生效。

5. 校验与验证方法 (Verification)

在任意终端运行:

$env:ANDROID_SDK_ROOT
$env:ANDROID_HOME

确保 ANDROID_SDK_ROOT 为空,或二者输出路径完全一致。构建时观察 Gradle 是否在 0 警告或无异常状态下顺利进入 Task 调度。


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Programming