Android 构建与跨平台发布
本页说明 OneDocs 基于 Tauri v2 的 Android 构建链路:从前置工具链、环境变量和 Rust Android targets,到本地 APK 构建、ABI 拆分、签名配置,以及前端针对 Android WebView 的构建目标适配。
Purpose and Scope
本页聚焦 Android 构建与跨平台发布所需的开发者流程和仓库内已声明的构建入口,适合需要在本地生成 APK、准备发布签名,或理解 Android 与桌面端前端产物差异的开发者。
覆盖范围包括:
- JDK 17、Android SDK/NDK 和 Rust Android targets 的准备;
package.json中的 Android 初始化、开发、通用构建和 ABI 拆分命令;- Vite 根据
TAURI_ENV_PLATFORM和VITE_BUILD_TARGET选择 Android/桌面编译目标的机制; - 本地签名所需的 keystore、
keystore.properties和 CI Secrets 约定; - APK 输出位置、首次构建成本和发布相关注意事项。
桌面端 Tauri 构建、应用功能本身、前端业务模块和完整 CI workflow 的实现不在本页展开;这里仅说明它们与 Android 发布链路直接相关的连接点。仓库的 Android 操作手册位于 docs/BUILD_ANDROID.md,脚本入口位于 package.json。
Overview
OneDocs 使用 Tauri v2 将前端应用包装为 Android 应用。Android 构建不是单一的 Vite 打包:它同时依赖 Java/Gradle、Android SDK/NDK、Rust 的 Android 交叉编译 targets,以及 Tauri CLI 生成和构建 Android 项目的能力。
仓库将构建职责分成两层:
- 前端构建层:Vite 负责解析 React 应用、生成
dist,并根据平台选择 JavaScript 编译目标。Android 默认使用es2021和chrome70,而非 Android 之外默认的esnext、chrome120和safari17。 - Tauri Android 层:
tauri android init初始化 Android 工程;tauri android dev启动 Android 开发模式;tauri android build负责生成 APK;追加--split-per-abi时按 ABI 拆分 APK。
这种分层的关键原因是 Android WebView 的运行时能力不同于现代桌面浏览器。vite.config.ts 明确指出较旧 Android 设备不支持 esnext/chrome120,因此平台默认 target 降级为 es2021/chrome70。同时,VITE_BUILD_TARGET 可以显式覆盖默认选择,支持单个 target 或逗号分隔的多个 target。
Architecture
图中的关系来自仓库实际脚本和配置:package.json 暴露 Tauri Android 命令;vite.config.ts 负责前端 target、React 插件、产物目录和 PDF.js 分块;Android 指南定义 JDK、SDK、NDK、Rust targets 和输出目录。tauri android init 先生成 src-tauri/gen/android,之后构建流程使用该 Android 工程和前端产物。
Sources:
构建前置条件与环境
JDK 与 Gradle 兼容性
Android 指南要求使用 JDK 17。文档同时说明项目使用 Gradle 8.9,Java 21 及以上版本可能导致 Unsupported class file major version 70 一类的 class-file 版本错误。因此,不能只设置 JAVA_HOME:如果系统 PATH 的前部仍然指向其他 Java 版本,Gradle 可能继续使用错误的运行时。正确做法是让 JDK 17 的 bin 位于 PATH 前部。
仓库文档给出的 Windows 环境变量约定是:
| 变量 | 示例/要求 | 用途 |
|---|---|---|
JAVA_HOME | JDK 17 安装目录 | 指定 Java 工具链 |
ANDROID_HOME | %LOCALAPPDATA%\\Android\\Sdk | 指定 Android SDK |
NDK_HOME | $ANDROID_HOME\\ndk\\27.0.12077973 | 指定 Android NDK |
如果已安装 Android Studio,也可以使用其内置 JBR 作为 JAVA_HOME;但文档仍然强调实际构建时必须确保使用的是兼容的 Java 17 运行时。
Rust Android targets
本地构建前需要安装四个 Rust 目标:aarch64-linux-android、armv7-linux-androideabi、i686-linux-android 和 x86_64-linux-android。这组 targets 对应 Android 构建中常见的 ARM64、ARMv7、x86 和 x86_64 架构;如果只验证 ARM64,可以按照文档建议使用 --target aarch64 缩短首次构建时间,但仓库本身的默认脚本仍然提供通用构建和 ABI 拆分两种入口。
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-androidSource:
BUILD_ANDROID.md
脚本入口与实际构建流程
package.json 将 Tauri CLI 命令封装为 npm scripts,避免开发者直接记忆完整的 CLI 调用:
1"scripts": {
2 "android:init": "tauri android init",
3 "android:dev": "tauri android dev",
4 "android:build": "tauri android build",
5 "android:build:split": "tauri android build -- --split-per-abi",
6 "android:build:signed": "tauri android build && node scripts/rename-apk.mjs"
7}Source:
package.json
这些命令的职责不同:
npm run android:init:只需初始化一次,用于生成 Android 工程;npm run android:dev:进入 Android 开发模式,适合联调;npm run android:build:执行通用 Android 构建;npm run android:build:split:向 Tauri 构建命令传入--split-per-abi,生成按 ABI 拆分的 APK,单个文件体积更小;npm run android:build:signed:先执行 Tauri 构建,再调用仓库中的 APK 重命名脚本。该脚本在package.json中被引用,但其实现未在本页已读取的源材料中展开,因此不能据此推断重命名规则。
典型生命周期是“初始化 → 配置签名 → 开发验证 → 构建发布”:
Sources:
前端跨平台编译目标
vite.config.ts 先读取 VITE_BUILD_TARGET,再判断 TAURI_ENV_PLATFORM 是否为 android。选择顺序如下:
- 如果设置了非空的
VITE_BUILD_TARGET且不包含逗号,直接使用该字符串; - 如果包含逗号,按逗号分割、去除空白并过滤空项,形成 target 数组;
- 没有显式 target 且平台为 Android 时,使用
["es2021", "chrome70"]; - 其他平台默认使用
["esnext", "chrome120", "safari17"]。
这意味着显式 VITE_BUILD_TARGET 会覆盖 Android 的安全默认值。修改该变量时,应确认目标 Android WebView 支持对应语法和浏览器特性,否则可能把前端编译成设备无法执行的代码。
除 target 选择外,Android/桌面共用以下 Vite 行为:使用 React 插件;通过 @ alias 指向 src;将输出写入 dist;在非 TAURI_DEBUG 模式启用 esbuild 压缩,在调试模式生成 sourcemap;并把 pdfjs-dist 放入名为 pdfjs 的 manual chunk。这里的 PDF.js 分块是构建产物组织策略,不是 Android 专属逻辑。
1const buildTargetEnv = process.env.VITE_BUILD_TARGET?.trim();
2const isAndroid = process.env.TAURI_ENV_PLATFORM === "android";
3const buildTarget = (() => {
4 if (buildTargetEnv) {
5 if (!buildTargetEnv.includes(",")) {
6 return buildTargetEnv;
7 }
8 return buildTargetEnv.split(",").map((item) => item.trim()).filter(Boolean);
9 }
10
11 // Android WebView on older devices doesn't support esnext/chrome120
12 // Chrome 70 covers Android 10+; es2021 is widely supported
13 if (isAndroid) {
14 return ["es2021", "chrome70"];
15 }
16
17 return ["esnext", "chrome120", "safari17"];
18})();Source:
vite.config.ts
签名配置与发布安全
发布 APK 必须签名。仓库指南把签名分为一次性密钥生成、本地构建配置和 CI Secrets 三个步骤。
生成 keystore
文档要求使用 JDK 17 提供的 keytool,在项目根目录生成 upload-keystore.jks,别名为 upload,RSA 密钥长度为 2048,有效期为 10000 天。该文件应妥善保管且不能提交到版本控制。CI 场景下,文档给出将 keystore 转为 Base64 的方式,以便存入 GitHub Actions Secrets。
本地 keystore.properties
本地构建前需要在 src-tauri/gen/android 下生成 keystore.properties,其中包含 keyAlias、password 和 storeFile。指南特别给出以无 BOM UTF-8 写入该文件的 PowerShell 示例,原因是 Gradle 属性文件需要稳定的编码和路径值。storeFile 可以是 keystore 的绝对路径。
配置项
| 配置项 | 类型 | 默认值/状态 | 说明 |
|---|---|---|---|
ANDROID_KEY_BASE64 | Secret/string | 无,发布 CI 时配置 | Base64 编码的 keystore 内容 |
ANDROID_KEY_ALIAS | Secret/string | 文档示例为 upload | keystore 中使用的 alias |
ANDROID_KEY_PASSWORD | Secret/string | 无,使用者设置 | keystore 密码 |
ANDROID_KEYSTORE_PATH | .env 配置 | 示例为 upload-keystore.jks | 本地 keystore 路径 |
keyAlias | keystore.properties 属性 | upload | Android Gradle 签名 alias |
password | keystore.properties 属性 | 使用者设置 | Android Gradle 签名密码 |
storeFile | keystore.properties 属性 | 使用者路径 | keystore 文件路径 |
.env 中的前三项用于本地/CI 约定,GitHub Actions Secrets 至少需要 ANDROID_KEY_BASE64、ANDROID_KEY_ALIAS 和 ANDROID_KEY_PASSWORD;ANDROID_KEYSTORE_PATH 是指南列出的 .env 路径配置。仓库文档没有在已读取材料中给出 workflow 如何消费这些变量的逐步实现,因此 CI 内部解码和注入细节应以实际 workflow 为准。
产物、性能与运维注意事项
- APK 产物位于
src-tauri/gen/android/app/build/outputs/apk/;构建完成后应从该目录检查实际文件。 android:build:split通过 ABI 拆分降低单个 APK 体积,但分发时需要根据设备架构选择对应文件;通用构建更适合需要一个覆盖多架构入口的场景。- 首次构建需要下载 Gradle 依赖并编译 Rust 交叉目标,耗时会明显高于后续构建。优先只构建
aarch64可以缩短验证周期,但不能替代完整多架构发布验证。 - Android 默认的
es2021/chrome70target 是兼容性策略。若通过VITE_BUILD_TARGET覆盖它,应在目标 Android WebView 上验证启动、路由、Markdown/PDF 等关键功能。 - 签名密钥是发布身份的一部分。丢失 keystore 或密码会影响后续同一应用的发布更新;不要把
.jks、密码或未脱敏的keystore.properties提交到仓库。
Failure Modes、边界条件与扩展点
常见失败模式
- Java 版本错误:JDK 不是 17,或 PATH 中其他 Java 优先级更高,可能在 Gradle 阶段出现 class-file major version 错误。修复时同时检查
JAVA_HOME和PATH。 - 工具链变量缺失:
ANDROID_HOME或NDK_HOME未指向实际安装目录时,Tauri/Gradle 无法定位 Android 编译工具。 - Rust target 缺失:目标架构未通过
rustup target add安装时,对应 ABI 的 Rust 部分无法交叉编译。 - 签名文件缺失或编码不正确:没有
keystore.properties、路径错误、密码不匹配,或文件编码不符合预期,会使签名构建失败。 - 前端 target 过新:显式覆盖 Android target 后,旧 WebView 可能无法解析产物。该风险来自配置中的平台兼容性注释和默认 target 设计。
并发与一致性
已读取的 Android 构建文档和构建配置没有实现共享状态、后台队列或并发锁;因此本页无法声称构建过程具有额外的并发协调机制。实际构建主要依赖 Gradle、Rust 和 Tauri 工具链自身的缓存与任务调度。发布时应避免多个任务同时写入同一个生成目录或签名配置文件,以免产生难以复现的中间产物问题。
扩展点
- 通过新增或调整
package.jsonscripts,可以封装新的 Tauri CLI 参数; - 通过
VITE_BUILD_TARGET可以为特定设备/运行时显式选择 Vite target; - 通过 Tauri CLI 的 Android 参数可以扩展构建模式,但应保持与
vite.config.ts的平台判断一致; vite.config.ts的manualChunks可继续承载大型依赖的拆分策略,但这属于通用前端构建优化,不应与签名流程混合。
API Reference
本页主题没有 HTTP API。可操作的公开入口是 npm scripts:
| 命令 | 参数 | 行为 | 返回/产物 |
|---|---|---|---|
npm run android:init | 无 | 初始化 Tauri Android 工程 | src-tauri/gen/android |
npm run android:dev | 无 | 启动 Android 开发模式 | 运行中的 Android 开发应用 |
npm run android:build | 无 | 构建通用 APK | src-tauri/gen/android/app/build/outputs/apk/ 下的 APK |
npm run android:build:split | 内部传入 --split-per-abi | 按 ABI 拆分 APK | 按架构区分的 APK |
npm run android:build:signed | 无 | 构建后执行 scripts/rename-apk.mjs | 重命名行为取决于该脚本实现 |
Related Links
配置覆盖关系与发布检查清单
Vite 的 target 选择存在明确的覆盖优先级:显式 VITE_BUILD_TARGET 最高;没有显式值时,Android 平台使用兼容性默认值;非 Android 平台使用桌面默认值。这个优先级比“按平台硬编码”更灵活,但也意味着发布脚本或 CI 若注入了 VITE_BUILD_TARGET,可能改变 Android 默认兼容策略。
Source:
vite.config.ts
正式发布前可按以下顺序检查:
- 确认
JAVA_HOME和PATH都指向 JDK 17; - 确认
ANDROID_HOME、NDK_HOME可访问,且 NDK 版本与本地环境一致; - 确认需要发布的 Rust Android targets 已安装;
- 首次使用时执行
npm run android:init; - 本地签名构建前确认
src-tauri/gen/android/keystore.properties的 alias、密码和路径有效; - 先用
npm run android:dev验证 Android 运行时,再执行通用或 ABI 拆分构建; - 从
src-tauri/gen/android/app/build/outputs/apk/检查 APK,并根据分发策略选择通用或 ABI 拆分产物; - CI 发布时只通过 Secrets 提供签名材料,不把 keystore 或密码写入版本库。
如果构建失败但不属于上述工具链、target 或签名问题,当前已读取的源材料不足以确定具体原因;应继续检查 Tauri 生成的 Android 工程、Gradle 日志以及仓库中实际的 workflow 实现。