Capacitor8-Android打包完整手册

文章目录

    • [0. 总览](#0. 总览)
      • [0.1 版本锁定表](#0.1 版本锁定表)
      • [0.2 目录规划](#0.2 目录规划)
      • [0.3 四段依赖顺序](#0.3 四段依赖顺序)
    • [1. 阶段 A · 基础运行时](#1. 阶段 A · 基础运行时)
      • [1.1 JDK 21](#1.1 JDK 21)
      • [1.2 Node.js 22 LTS](#1.2 Node.js 22 LTS)
      • [1.3 Git](#1.3 Git)
      • [1.4 PowerShell 脚本策略(条件性)](#1.4 PowerShell 脚本策略(条件性))
      • [1.5 环境变量(用户变量)](#1.5 环境变量(用户变量))
      • [1.6 验收 A](#1.6 验收 A)
      • [1.7 常见坑](#1.7 常见坑)
    • [2. 阶段 B · Android SDK(不装 IDE)](#2. 阶段 B · Android SDK(不装 IDE))
      • [2.1 下载命令行工具](#2.1 下载命令行工具)
      • [2.2 目录归位(整套流程最容易错的一步)](#2.2 目录归位(整套流程最容易错的一步))
      • [2.3 安装 SDK 三件套(必须用 CMD)](#2.3 安装 SDK 三件套(必须用 CMD))
      • [2.4 接受许可](#2.4 接受许可)
      • [2.5 验收 B](#2.5 验收 B)
      • [2.6 常见坑](#2.6 常见坑)
    • [3. npm 配置](#3. npm 配置)
    • [4. 阶段 C · demo 项目创建](#4. 阶段 C · demo 项目创建)
      • [4.1 建项目](#4.1 建项目)
      • [4.2 `vite.config` 加 `base`(防白屏的兼容性兜底)](#4.2 vite.config 加 base(防白屏的兼容性兜底))
      • [4.3 装 Capacitor 8 三个包](#4.3 装 Capacitor 8 三个包)
      • [4.4 `cap init` 与配置文件](#4.4 cap init 与配置文件)
      • [4.5 `webDir` 对照表](#4.5 webDir 对照表)
      • [4.6 build → add → sync](#4.6 build → add → sync)
    • [5. APK 打包(debug)](#5. APK 打包(debug))
      • [5.1 首次构建前:换 Gradle 发行版镜像(关键修复)](#5.1 首次构建前:换 Gradle 发行版镜像(关键修复))
      • [5.2 构建](#5.2 构建)
      • [5.3 产物](#5.3 产物)
      • [5.4 构建告警(全部无害,不要为它们改文件)](#5.4 构建告警(全部无害,不要为它们改文件))
      • [5.5 装机与分发(可选)](#5.5 装机与分发(可选))
    • [6. 日常最小循环与禁令](#6. 日常最小循环与禁令)
    • [7. release 签名包](#7. release 签名包)
      • [7.1 两个密码是什么](#7.1 两个密码是什么)
      • [7.2 生成 keystore(只做一次)](#7.2 生成 keystore(只做一次))
      • [7.3 `keystore.properties`(手动新建,不会自动生成)](#7.3 keystore.properties(手动新建,不会自动生成))
      • [7.4 `build.gradle` 三处改动](#7.4 build.gradle 三处改动)
      • [7.5 `.gitignore` 追加两行](#7.5 .gitignore 追加两行)
      • [7.6 出包](#7.6 出包)
      • [7.7 验签与输出解读](#7.7 验签与输出解读)
      • [7.8 签名指纹基准(用于以后核对是否同一个 keystore)](#7.8 签名指纹基准(用于以后核对是否同一个 keystore))
      • [7.9 发布注意](#7.9 发布注意)
    • [8. 换成自己的项目](#8. 换成自己的项目)
    • [9. 排错速查总表](#9. 排错速查总表)
    • [10. 附录](#10. 附录)
      • [10.1 环境变量速查](#10.1 环境变量速查)
      • [10.2 关键路径速查](#10.2 关键路径速查)
      • [10.3 完整命令流水(新机复现用,按序执行)](#10.3 完整命令流水(新机复现用,按序执行))
      • [10.4 已知限制](#10.4 已知限制)

用途 :全新 Windows 电脑,从零搭建混合开发(Web 转 APK)环境,覆盖环境搭建、demo 项目创建、debug / release 出包、签名配置、排错速查。

实操验证 :2026-11-10 全流程跑通,未安装 Android Studio,未连接真机或模拟器。

核心原理 :Web 构建产物 → cap sync 拷入 Android 壳工程 → Gradle / AGP 编译 → APK(或 AAB)。

约定 :全程使用 CMD(命令提示符),路径全部放 D 盘、纯英文无空格。所有 npx cap 在项目根目录 执行,所有 gradlew.bat 在 android\ 目录执行。


0. 总览

0.1 版本锁定表

组件 锁定版本 说明
JDK Eclipse Temurin 21(21.0.13+9) Capacitor 8 用 Java 21 编译,装 17 会报「无效的源发行版: 21」
Node.js 22 LTS(v22.23.4) Capacitor 8 CLI 要求 node >= 22,20 直接不支持
npm 10.9.9(Node 22 自带) 无需升级
Git 2.56.0.4 可选,仅用于拉代码
Android SDK Platform 36 / build-tools 36.0.0 / platform-tools 37.0.1 compileSdk = targetSdk = 36
AGP / Gradle 8.13 / 8.14.3 由 cap add android 生成的 wrapper 锁定,不要全局装 Gradle
Capacitor 8.x(core / android / cli) minSdk = 24
脚手架 create-vite 9.3.4 engines ^20.19.0 or >=22.12.0,Node 22.23.4 兼容
Android Studio 不需要 只用 commandlinetools + gradlew

0.2 目录规划

bash 复制代码
D:\dev\jdk-21                         JDK 21(固定目录名,不带版本号)
D:\dev\nodejs                         Node.js
D:\dev\Git                            Git
D:\dev\Android\Sdk                    Android SDK(ANDROID_HOME)
D:\dev\gradle-home                    Gradle 缓存(GRADLE_USER_HOME)
D:\dev\npm-cache                      npm 缓存
D:\dev\projects\hello-app             项目
D:\dev\release.jks                    签名密钥库(务必异地备份)

0.3 四段依赖顺序

bash 复制代码
A 运行时  JDK21 / Node22 / Git / 环境变量 / npm 配置
   ↓ sdkmanager 是 Java 程序,必须先有 JAVA_HOME
B SDK     commandlinetools 归位 → 装三件套 → 接受 licenses
   ↓ Gradle 靠 ANDROID_HOME 找 SDK、靠 android-36 编译
C 项目    Vite → Capacitor 三包 → cap init → build → add → sync → assembleDebug
   ↓ 正式分发才需要
D 签名    keytool → keystore.properties → build.gradle → assembleRelease → apksigner

环境变量任何改动都必须关掉所有终端和 VSCode 重新打开,只对新建进程生效。


1. 阶段 A · 基础运行时

1.1 JDK 21

安装包:OpenJDK21U-jdk_x64_windows_hotspot_21.0.13_9.msi(Eclipse Temurin / adoptium.net)。

  • 为什么不用 Oracle JDK:Oracle JDK 21 的 21.0.12 及更早走 NFTC 免费许可,从 21.0.13 起改回 OTN(商业生产需订阅)。Temurin 走 GPLv2 + Classpath Exception,无免费窗口、可商用、可自由打补丁。两者功能等同,版本号相同但法律性质不同。
  • Custom Setup 页把安装目录改成 D:\dev\jdk-21。目录名不要带版本号 ,否则升级 JDK 后 JAVA_HOME 会失效。
  • 装完 where java 应只有一条,指向 D:\dev\jdk-21\bin\java.exe。

1.2 Node.js 22 LTS

安装包:node-v22.23.4-x64.msi,路径 D:\dev\nodejs\。

  • Custom Setup 页 5 项全部保留(Node.js runtime、corepack manager、npm package manager、Online documentation shortcuts、Add to PATH)。蓝色高亮只是「当前选中项显示说明」,不是只装这一项 ;尤其 Add to PATH 别动,MSI 靠它自动写 PATH。
  • Tools for Native Modules 页的 Automatically install the necessary tools 不要勾(会额外装 Chocolatey + Python + VS Build Tools,Capacitor 8 依赖全是纯 JS)。
  • MSI 会自动写 PATH,不用手动加 node 那一条。

1.3 Git

安装包:Git-2.56.0.4-64-bit.exe,路径 D:\dev\Git。

  • Adjusting your PATH environment 选第二项 Git from the command line and also from 3rd-party software 。第一项会让 CMD / PowerShell 认不到 git;第三项会用 Git 自带的 find/sort 覆盖 Windows 系统工具。
  • Choosing the default editor 保持默认 Vim 也没关系(打包链路不调用 Git 编辑器),以后随时可改:git config --global core.editor notepad。
  • 行尾转换保持默认 Checkout Windows-style, commit Unix-style。
  • 完成页取消 Launch Git Bash / View Release Notes。

1.4 PowerShell 脚本策略(条件性)

bash 复制代码
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

仅当使用 PowerShell 或 VSCode 内置终端时需要(否则 npx.ps1 cannot be loaded)。全程用 CMD 则完全不用设 ,CMD 走的是 npx.cmd。设一次永久生效。

1.5 环境变量(用户变量)

Win+R → sysdm.cpl → 高级 → 环境变量 → 用户变量:

变量 值
JAVA_HOME D:\dev\jdk-21
ANDROID_HOME D:\dev\Android\Sdk
GRADLE_USER_HOME D:\dev\gradle-home

Path 追加三条(Node / Git 由 MSI 自动写入,不要再手动加):

bash 复制代码
%JAVA_HOME%\bin
%ANDROID_HOME%\cmdline-tools\latest\bin
%ANDROID_HOME%\platform-tools

说明:SDK、gradle-home、cmdline-tools\latest\bin 此时还不存在也没关系,PATH 允许指向尚未创建的目录;%JAVA_HOME%\bin 建议放靠前位置,避免被其他 JDK 抢先。

1.6 验收 A

bash 复制代码
java -version
node -v
npm -v
git --version
where java
echo %JAVA_HOME%
echo %ANDROID_HOME%
echo %GRADLE_USER_HOME%
命令 期望
java -version openjdk 21.0.13 / Temurin
node -v v22.23.4
npm -v 10.9.9
git --version 2.56.0.4
where java 只有一条 D:\dev\jdk-21\bin\java.exe
三个 echo 分别输出三个 D 盘路径

1.7 常见坑

现象 处理
echo %ANDROID_HOME% 输出字面量 %ANDROID_HOME% 该 CMD 窗口在配置前就开着,环境变量是进程启动时的快照;关掉重开一个新窗口即可。也可用 set ANDROID 检查变量名是否拼错或带空格
where java 指向别的目录 把 %JAVA_HOME%\bin 上移到靠前位置;Oracle JDK 安装器会塞 javapath,Temurin 不会
npx.ps1 cannot be loaded 见 1.4

2. 阶段 B · Android SDK(不装 IDE)

2.1 下载命令行工具

commandlinetools-win-15859902_latest.zip(约 155MB)

直链:https://dl.google.com/android/repository/commandlinetools-win-15859902_latest.zip

2.2 目录归位(整套流程最容易错的一步)

zip 解压后里面是一个 cmdline-tools 文件夹,要把它里面的内容 (bin、lib、source.properties、NOTICE.txt)放进 <Sdk>\cmdline-tools\latest\:

text 复制代码
D:\dev\Android\Sdk\
└─ cmdline-tools\
   └─ latest\
      ├─ bin\sdkmanager.bat      ← 必须存在这个文件
      ├─ lib\
      └─ source.properties
  • 多套一层 cmdline-tools、或漏掉 latest,后面全线失败。
  • 若已解压到 D:\dev\cmdline-tools,先 Test-Path D:\dev\cmdline-tools\bin\sdkmanager.bat 确认层级,再整体剪切并改名为 latest;归位后 D:\dev\ 根目录不该再留 cmdline-tools。

2.3 安装 SDK 三件套(必须用 CMD)

bash 复制代码
sdkmanager --sdk_root=D:\dev\Android\Sdk "platform-tools" "platforms;android-36" "build-tools;36.0.0"
  • 包名含分号,双引号不能去掉 :PowerShell 传 .bat 时可能被 cmd 拆成两个参数,CMD 里双引号能保护它。
  • 首次提示 Accept? (y/N) 输入 y。下载几百 MB,100% Unzipping... 是正常解包过程,别关窗口。
  • sdkmanager 本身是 Java 程序,依赖 JAVA_HOME 或 PATH 里的 java.exe,所以环境变量要先配好。
  • 开头的 WARNING: sdkmanager is deprecated. Use Android CLI instead 可忽略 :Google 推荐新的 android sdk 命令,但 sdkmanager 仍完整可用,且 Gradle / AGP 只读 SDK 目录文件,与用哪个 CLI 装包无关。
  • 超时就重跑同一条命令,已装好的包会跳过;dl.google.com 不通时挂代理或换镜像。

2.4 接受许可

bash 复制代码
sdkmanager --sdk_root=D:\dev\Android\Sdk --licenses

一路 y(约 6--8 次),直到 All SDK package licenses accepted。漏这步 Gradle 会报 licenses not accepted。

2.5 验收 B

bash 复制代码
adb version
sdkmanager --sdk_root=D:\dev\Android\Sdk --list_installed

list_installed 三项齐全(本机实测输出):

Path Version
build-tools;36.0.0 36.0.0
platform-tools 37.0.1
platforms;android-36 2

adb version 会显示 Installed as D:\dev\Android\Sdk\platform-tools\adb.exe,同时验证 PATH 生效。

2.6 常见坑

现象 处理
sdkmanager 不是内部命令 2.2 目录结构错,或没重开终端
ERROR: JAVA_HOME is not set 补 1.5 的环境变量并重开终端
Could not determine SDK root 命令补 --sdk_root=D:\dev\Android\Sdk,核对 ANDROID_HOME
platforms is not a valid package 换 CMD + 双引号,别用 PowerShell
licenses not accepted 补跑 2.4

3. npm 配置

bash 复制代码
npm config set registry https://registry.npmmirror.com
npm config set cache D:\dev\npm-cache
npm config get registry
npm config get cache
  • 不要加 --global :会写到 Node 安装目录下的全局 npmrc,可能需要管理员权限且升级 Node 会丢。上面两条写进用户级 C:\Users\<你>\.npmrc。
  • npm config get(不带参数)列出全部生效配置,应出现 ; "user" config from ...\.npmrc 段和 cache / registry 两行。
  • prefix = C:\Users\<你>\AppData\Roaming\npm 是内建默认值,只影响 npm i -g;全程用本地 devDependency + npx,不会往这里写,不用改。
  • npmmirror 对新发版本偶有同步滞后,装 @capacitor/cli@^8 报 404 时切回官方源:npm config set registry https://registry.npmjs.org。

明确不用做 :升级 npm、装 nvm / corepack / pnpm / yarn、配 NODE_PATH / NODE_ENV / prefix、全局装 @capacitor/cli、装 Python 或 VS Build Tools、全局装 Gradle。


4. 阶段 C · demo 项目创建

4.1 建项目

bash 复制代码
cd /d D:\dev\projects
npm create vite@latest hello-app -- --template vue
cd hello-app
npm install

交互应答:Ok to proceed? (y) 输 y;若问框架选 Vue → JavaScript;若问 Install with npm and start now? 选 No 。成功标志:Scaffolding project in ... + Done. Now run:,随后 npm install 输出 added xx packages 且无 ERR!。

4.2 vite.config 加 base(防白屏的兼容性兜底)

打开 hello-app\vite.config.js(TS 模板是 .ts),在 defineConfig({ ... }) 里加一行:

js 复制代码
export default defineConfig({
  base: './',
  plugins: [vue()],
})
  • base 只改构建产物里 <script> / <link> / 动态 import 的 URL 前缀:/assets/x.js → ./assets/x.js。不改源码、不改打包内容。
  • 自检:npm run build 后打开 dist\index.html 看 src= 开头是 /assets/ 还是 ./assets/。
  • 准确定位 :Capacitor 6+ 默认 androidScheme: https,WebView 加载 https://localhost/,原生层 WebViewLocalServer 拦截请求并从 assets/public 返回字节流,所以绝对路径默认也能加载。base: './' 是低成本保险,不是硬性必需 ;它真正防的是 file:// 容器(Cordova / 自建 WebView)和网站子目录部署。
  • 什么时候别加 :Vue Router / React Router 的 history 模式且有深层路由时,./ 相对「当前 URL」解析,从 /user/profile 刷新会找 /user/assets/x.js → 404。这类项目保持 base: '/' 或改用 createWebHashHistory()。
项目情况 建议
单页 / hash 路由 / 只做 APK base: './'
history 路由、深层路径 保持 /,或换 hash 路由
同时部署到网站子目录 用明确的 base: '/子目录/'

4.3 装 Capacitor 8 三个包

bash 复制代码
npm i "@capacitor/core@^8" "@capacitor/android@^8"
npm i -D "@capacitor/cli@^8"

CMD 里双引号不能去掉 :^ 是 CMD 转义符,不加引号 @^8 会被吞成 @8。node_modules 是项目级依赖,换项目要重装。

4.4 cap init 与配置文件

bash 复制代码
npx cap init "HelloApp" "com.example.helloapp" --web-dir=dist
  • 生成 capacitor.config.json (本项目用 --template vue 的 JS 模板,未装 TypeScript)。只有项目里能找到 typescript 包时才生成 capacitor.config.ts,且此时 package.json 需要 "type": "module"(否则 Parsing capacitor.config.ts failed)。别同时保留两份配置。
  • 位置在项目根目录 ,与 package.json 同级,不在 android\ 也不在 src\。
  • 内容(83 字节):
json 复制代码
{
  "appId": "com.example.helloapp",
  "appName": "HelloApp",
  "webDir": "dist"
}
  • cap sync 打印的 Creating capacitor.config.json in android\app\src\main\assets 是自动生成的运行时副本,每次 sync 覆盖,不要手改。要编辑的只有项目根那一个。
  • appId 上架后不可改。

4.5 webDir 对照表

webDir 必须等于 npm run build 实际输出、且含最终 index.html 的目录名;cap sync 会把它整体拷到 android\app\src\main\assets\public\。

构建工具 输出目录
Vite / Vue CLI dist
Create React App build
Nuxt 静态导出 .output/public
Angular dist\<项目名>\browser

不确定就先跑一次 npm run build,看哪个新目录里出现 index.html。配错或忘了 sync,结果就是启动白屏。

4.6 build → add → sync

bash 复制代码
npm run build
npx cap add android
npx cap sync android
  • cap add android 生成 android\ 原生工程,整个项目生命周期只跑一次 ;重复执行会重建并覆盖你在 android\ 里的改动。
  • cap sync 等价于 copy + update,成功标志:✔ Copying web assets from dist to android\app\src\main\assets\public 等五项全绿。
  • 别跑 npx cap open android,它会启动本机没装的 Android Studio。

5. APK 打包(debug)

5.1 首次构建前:换 Gradle 发行版镜像(关键修复)

首次 gradlew 会从 services.gradle.org 下载 Gradle 8.14.3,国内易触发 wrapper 默认 10 秒超时:

bash 复制代码
java.io.IOException: Downloading from https://services.gradle.org/distributions/gradle-8.14.3-all.zip failed
SocketTimeoutException: Read timed out

用记事本打开 android\gradle\wrapper\gradle-wrapper.properties (注意是两层子目录;Windows 隐藏扩展名后可能只显示成 gradle-wrapper):

bash 复制代码
dir /s /b gradle-wrapper.properties
notepad D:\dev\projects\hello-app\android\gradle\wrapper\gradle-wrapper.properties

把 distributionUrl 整行换成:

properties 复制代码
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14.3-all.zip
  • \: 是 properties 的转义写法,照原样保留 ,别改成 https://;其它行一律不动。
  • 备用:https\://mirrors.huaweicloud.com/gradle/gradle-8.14.3-all.zip(腾讯云 / 华为云 / 阿里云均实测 200)。
  • .properties 注释规则:行首 # 或 ! 才是注释;禁行尾注释 (会被当成 URL 的一部分)、禁中文 (按 ISO-8859-1 读会乱码);别把 distributionUrl 注释掉。
  • 换 URL 后 wrapper 按 distributionUrl 字符串算缓存目录名,会新建目录重新下载,旧的 .part / .lck 残留不用管。

两类下载要分清:

Gradle 发行版 项目依赖(AGP / androidx / Kotlin)
来源 distributionUrl(腾讯云镜像) dl.google.com / mavenCentral()
落盘 D:\dev\gradle-home\wrapper\dists\ D:\dev\gradle-home\caches\modules-2\
解决方式 改 gradle-wrapper.properties 在 D:\dev\gradle-home\init.gradle 配仓库镜像(阿里云 `maven.aliyun.com/repository/google

5.2 构建

bash 复制代码
cd /d D:\dev\projects\hello-app\android
gradlew.bat assembleDebug --no-daemon

assembleDebug 是构建任务;wrapper 先检查本地有无 Gradle 8.14.3,没有则自动下载解压后才开始构建。不会装到系统、不写 PATH、不需要全局 Gradle。首次约 4--15 分钟(本机实测 BUILD SUCCESSFUL in 4m 11s,93 tasks executed),别强关。

缓存目录树:

text 复制代码
D:\dev\gradle-home\
├─ wrapper\dists\gradle-8.14.3-all\<hash>\
│   ├─ gradle-8.14.3-all.zip
│   ├─ gradle-8.14.3\bin\gradle.bat
│   └─ gradle-8.14.3-all.zip.ok
├─ caches\modules-2\files-2.1\
└─ daemon\

5.3 产物

bash 复制代码
D:\dev\projects\hello-app\android\app\build\outputs\apk\debug\app-debug.apk
bash 复制代码
dir /s /b app\build\outputs\apk\*.apk

5.4 构建告警(全部无害,不要为它们改文件)

输出 性质
Using flatDir should be avoided... Capacitor 模板自带的 aar 仓库写法
使用或覆盖了已过时的 API(中文乱码) javac 的 deprecation 提示,乱码只是 JDK 日志编码与 CMD 代码页不匹配
single-use Daemon ... will be stopped --no-daemon 触发的一次性进程正常退出
Incubating Problem report... Gradle 8 新特性提示

5.5 装机与分发(可选)

bash 复制代码
adb devices
adb install -r app\build\outputs\apk\debug\app-debug.apk
  • 手机需开「开发者选项 → USB 调试」,首次连接弹「允许 USB 调试」。
  • List of devices attached 为空 + no devices/emulators found 只是没连设备,与出包完全无关 :assembleDebug 是纯本地 Gradle 构建,全程不调用 adb。
  • debug 包带自动生成的 debug 签名,可以直接把文件发给别人安装;不可上架(商店要 AAB 且拒绝 debug 签名);换一台电脑重新打包后覆盖安装会报签名不一致,需先卸载。
  • 拷出分发:copy app\build\outputs\apk\debug\app-debug.apk D:\dev\hello-app-debug.apk

6. 日常最小循环与禁令

改了前端代码后,只需这三行(第二次起 Gradle 增量构建,快很多):

bash 复制代码
cd /d D:\dev\projects\hello-app
npm run build && npx cap sync android
cd /d D:\dev\projects\hello-app\android && gradlew.bat assembleDebug --no-daemon

用绝对路径 cd /d ...\android 而非相对 cd android,避免同一窗口重复执行时「已经在 android 里」导致失败。

四条禁令

禁止 原因
重复 npx cap add android 重建原生工程,覆盖 android\ 里的改动
npx cap open android 会启动本机没装的 Android Studio
npm i -g @capacitor/cli / 全局装 Gradle 本地 npx + 项目 gradlew 已锁版本
改了配置却跳过 npm run build 直接 cap sync 拷进去的还是旧产物

可选命令:

命令 用途
npx cap copy android 只拷 web 资源
npx cap update android 只更新原生插件
npx cap ls / npx cap doctor 列平台 / 环境体检
gradlew.bat clean assembleDebug --no-daemon 缓存异常时清理重来
gradlew.bat help --no-daemon 秒级只解析构建脚本,验证 build.gradle 语法
gradlew.bat bundleRelease --no-daemon 上架用 AAB

7. release 签名包

7.1 两个密码是什么

release.jks 是一个「盒子」,盒子里装着 mykey 这把私钥:

口令 保护对象 填错时报错
storePassword(密钥库口令) 打开整个 jks 文件 Keystore was tampered with, or password was incorrect
keyPassword(密钥口令) 使用 mykey 私钥签名 Cannot recover key / Failed to read key

一个 keystore 可放多把 alias 的密钥,所以设计了分层口令。但 JDK 21 的 keytool 默认生成 PKCS12,该格式不支持库口令与密钥口令不同 ;交互里 输入 <mykey> 的密钥口令 直接回车 = 与库密码相同。

结论:只需设一个强密码 ,keystore.properties 两行填同一个值即可 ------ Gradle 的 signingConfig 接口要求分别给出两个字段,不代表要两个不同密码。

7.2 生成 keystore(只做一次)

bash 复制代码
D:\dev\jdk-21\bin\keytool.exe -genkeypair -v -keystore D:\dev\release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias mykey

交互顺序:密钥库口令 → 再输一次 → 姓名 / 组织 / 城市 / 省市 / 国家代码(可填 CN)→ y 确认 → 问「输入 <mykey> 的密钥口令」时直接回车。

  • 一路回车那些 DN 字段会得到 CN=Unknown, OU=Unknown, ...,不影响安装、商店也不校验 DN 内容。想填真实信息可在发布前重新生成。
  • -validity 10000 约 27 年。本机实测产物 2742 字节。
  • keystore 一旦分发/上架就绝不能换 ,否则老用户无法覆盖升级,只能换 appId 重新上架。

7.3 keystore.properties(手动新建,不会自动生成)

用 notepad 打开不存在的文件,弹「找不到文件,是否创建?」点 是 ,粘内容后 Ctrl+S:

bash 复制代码
notepad D:\dev\projects\hello-app\android\keystore.properties
properties 复制代码
storeFile=D:/dev/release.jks
storePassword=<你设的密码>
keyAlias=mykey
keyPassword=<同一个密码>
  • 必须放在 hello-app\android\ (与 gradlew.bat、settings.gradle 同级),因为代码里 rootProject.file("keystore.properties") 的 rootProject 是 android\ 这一层。放到项目根会报 FileNotFoundException。
  • storeFile 用正斜杠 ,.properties 里反斜杠会被当转义符。
  • 密码别用 #、!、空格开头。
  • 该文件含明文密码,不进版本库。
  • 记事本「另存为」时保存类型选「所有文件」,别变成 keystore.properties.txt。
  • 本文档不记录真实密码,请自行在密码管理器保存。

7.4 build.gradle 三处改动

先确认是 Groovy 版并备份:

bash 复制代码
dir app\build.*
copy app\build.gradle app\build.gradle.bak
notepad app\build.gradle
groovy 复制代码
// ① 文件最顶部,apply plugin 之前
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(rootProject.file("keystore.properties")))

// ② android { 块内、buildTypes 之前
signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
    }
}

// ③ 已有 buildTypes { release { ... } } 里加一行,其余 minifyEnabled 等保持不动
signingConfig signingConfigs.release

注释规则(三类文件不要混):

文件 语言 规则
build.gradle / .kts Groovy / Kotlin 支持 //、/* */、行尾注释,中文一般也行(Gradle 8 按 UTF-8 编译、JDK 21 默认 UTF-8)
*.properties Java Properties 只能行首 # / !;禁行尾注释、禁中文
capacitor.config.json JSON 完全不支持注释

改完先做秒级语法自检,不用等完整构建:

bash 复制代码
gradlew.bat help --no-daemon

BUILD SUCCESSFUL 即脚本语法没问题;报 Could not compile build file 就把报错按 9 章排查。若实际是 build.gradle.kts,上述写法要换 Kotlin DSL(import java.util.Properties + val keystoreProperties = Properties().apply { load(...) } + signingConfigs { create("release") { ... } })。

7.5 .gitignore 追加两行

gitignore 复制代码
android/keystore.properties
*.jks

7.6 出包

bash 复制代码
cd /d D:\dev\projects\hello-app\android
gradlew.bat assembleRelease --no-daemon

产物:app\build\outputs\apk\release\app-release.apk

陷阱 :没接上 signingConfig 时 assembleRelease 也会 BUILD SUCCESSFUL,但产出 app-release-unsigned.apk,装不上。所以必须做下一步验签。

7.7 验签与输出解读

bash 复制代码
dir /s /b app\build\outputs\apk\release\*.apk
D:\dev\Android\Sdk\build-tools\36.0.0\apksigner.bat verify --verbose --print-certs app\build\outputs\apk\release\app-release.apk

本机实测结果(判据):

输出 判定
Verifies 签名完整、APK 未被篡改,可安装分发
Verified using v2 scheme: true 主签名方案生效,Android 7.0+ 全支持
v1 scheme (JAR signing): false 预期。minSdk 24,AGP 8 默认关闭 v1(v2 已覆盖所有可装设备)
v3 / v3.1 / v4 / SourceStamp: false 预期 。v3 用于密钥轮换、v4 是增量安装的 .idsig、SourceStamp 是 Play 溯源标记,普通 assembleRelease 不生成
Number of signers: 1 + RSA 2048 + 三种摘要齐全 证书就是 7.2 生成的 mykey,未混入 debug 签名
Signer #1 certificate DN: CN=Unknown, ... keytool 交互回车的默认值,功能无影响

apksigner 能打开 app-release.apk 本身就证明文件名不是 -unsigned,也证明 7.3 / 7.4 两步都做对了(Gradle 读不到凭据或没接 signingConfig,产不出这个文件)。

7.8 签名指纹基准(用于以后核对是否同一个 keystore)

后续每次发版若指纹一致,说明签名未变、可以覆盖升级。本机首次 release 包实测:

text 复制代码
SHA-256: 3e89aeb68410e7f6639cdda03a3ac25e77495490209d846db74129af3435fab1
SHA-1  : be6f7c28f08488fe1c2efa0713ffd178c515948c
MD5    : 6e61c9579cd712dc97cb5cd93e39dfeb

7.9 发布注意

  • 异地备份 D:\dev\release.jks + 密码,两者缺一不可;密码不写进代码或文档。
  • 每次发版把 app\build.gradle → defaultConfig → versionCode 加 1。
  • debug 包与 release 包签名不同,装过旧 debug 包的手机要先卸载再装,否则报签名冲突。
  • 上架商店要 AAB 而非 APK:gradlew.bat bundleRelease --no-daemon → app\build\outputs\bundle\release\app-release.aab。
  • 以后出 release 包只需第 6 章那三行(第 3 行换成 assembleRelease),7.2--7.5 不再重复。

8. 换成自己的项目

分层原则:机器级装一次所有项目共用;项目级每个新项目要重做。

层级 换项目时
JDK / SDK / 环境变量 / Gradle 缓存 / npm 镜像 完全不用动
Capacitor 三个 npm 包(node_modules 每项目独立) 必须重新 npm i
cap init 的 appId / appName / webDir 换成真实值,appId 上架后不可改
cap add android 每个项目只一次
vite.config 的 base 按该项目路由情况决定(见 4.2)
keystore.properties / build.gradle 签名配置 新项目重新做一遍 7.3--7.5;可复用同一个 release.jks(同签名才能升级)

完整命令(D:\dev\projects\my-app 为示例):

bash 复制代码
cd /d D:\dev\projects\my-app
npm install
npm i "@capacitor/core@^8" "@capacitor/android@^8"
npm i -D "@capacitor/cli@^8"
npx cap init "真实应用名" "com.company.myapp" --web-dir=dist
npm run build
npx cap add android
npx cap sync android
cd /d D:\dev\projects\my-app\android
gradlew.bat assembleDebug --no-daemon

--web-dir=dist 按 4.5 对照表换成实际目录。项目路径保持纯英文;中文路径构建失败时可在 android\gradle.properties 加 android.overridePathCheck=true。


9. 排错速查总表

现象 处理
echo %VAR% 输出字面量 重开终端(环境变量快照)
requires Java 17 / 无效的源发行版: 21 JAVA_HOME 未生效或指向 17,where java 核对并上移 Path
npx.ps1 cannot be loaded PowerShell 里 Set-ExecutionPolicy -Scope CurrentUser RemoteSigned,或改用 CMD
sdkmanager 不是内部命令 检查 cmdline-tools\latest\bin 层级 / 重开终端
platforms is not a valid package 换 CMD + 双引号
licenses not accepted 补 sdkmanager --licenses
platforms;android-36 找不到 用最新 commandlinetools 重新归位;sdkmanager --list 查可用包
@^8 被吞成 @8 CMD 里加双引号
@capacitor/cli 404 npmmirror 同步滞后,切 registry.npmjs.org 重装
Parsing capacitor.config.ts failed 老项目缺 "type": "module",或改用 .json;别同时保留两份
启动白屏 webDir 与构建产物目录不一致 / 忘了 npm run build / 忘了 cap sync;再查 base
Could not determine SDK root / SDK location not found 核对 ANDROID_HOME;或在 android\local.properties 写 sdk.dir=D:/dev/Android/Sdk(正斜杠)
Downloading ...gradle-8.14.3-all.zip failed / Read timed out 改 distributionUrl 为腾讯云镜像(5.1)
Maven 依赖阶段超时 在 D:\dev\gradle-home\init.gradle 配仓库镜像,不要改项目 build.gradle
FINDSTR: 无法打开 app\build.gradle CMD 停在项目根了;写 android\app\build.gradle 或先 cd /d ...\android
FileNotFoundException: keystore.properties 文件放错层(应在 android\)或存成了 .txt
Keystore was tampered with, or password was incorrect storePassword 错
Cannot recover key keyPassword 错,或 keyAlias 不是 mykey
产物是 app-release-unsigned.apk signingConfig 没接进 buildTypes.release
apksigner 报 DOES NOT VERIFY 跑了缓存旧包,gradlew.bat clean assembleRelease --no-daemon
adb: no devices/emulators found 只是没连设备,与出包无关,可忽略
中文路径构建失败 android\gradle.properties 加 android.overridePathCheck=true
覆盖安装报签名不一致 debug / release 签名不同,或换了 keystore;先卸载旧版

10. 附录

10.1 环境变量速查

bash 复制代码
JAVA_HOME        = D:\dev\jdk-21
ANDROID_HOME     = D:\dev\Android\Sdk
GRADLE_USER_HOME = D:\dev\gradle-home

Path 追加:
  %JAVA_HOME%\bin
  %ANDROID_HOME%\cmdline-tools\latest\bin
  %ANDROID_HOME%\platform-tools

10.2 关键路径速查

用途 路径
sdkmanager D:\dev\Android\Sdk\cmdline-tools\latest\bin\sdkmanager.bat
apksigner D:\dev\Android\Sdk\build-tools\36.0.0\apksigner.bat
keytool D:\dev\jdk-21\bin\keytool.exe
Gradle wrapper 配置 android\gradle\wrapper\gradle-wrapper.properties
项目配置 hello-app\capacitor.config.json
签名参数 hello-app\android\keystore.properties
debug APK android\app\build\outputs\apk\debug\app-debug.apk
release APK android\app\build\outputs\apk\release\app-release.apk
release AAB android\app\build\outputs\bundle\release\app-release.aab

10.3 完整命令流水(新机复现用,按序执行)

bash 复制代码
REM ---- 1. 安装三个 msi/exe(图形向导,见 1.1--1.3) ----
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
REM ---- 2. 配 1.5 的环境变量,关掉所有终端重开 ----
java -version
node -v
git --version
where java
echo %ANDROID_HOME%

REM ---- 3. SDK(commandlinetools 归位后,CMD) ----
sdkmanager --sdk_root=D:\dev\Android\Sdk "platform-tools" "platforms;android-36" "build-tools;36.0.0"
sdkmanager --sdk_root=D:\dev\Android\Sdk --licenses
adb version
sdkmanager --sdk_root=D:\dev\Android\Sdk --list_installed

REM ---- 4. npm ----
npm config set registry https://registry.npmmirror.com
npm config set cache D:\dev\npm-cache

REM ---- 5. 项目 ----
cd /d D:\dev\projects
npm create vite@latest hello-app -- --template vue
cd hello-app
npm install
REM 手工在 vite.config.js 加 base: './',
npm i "@capacitor/core@^8" "@capacitor/android@^8"
npm i -D "@capacitor/cli@^8"
npx cap init "HelloApp" "com.example.helloapp" --web-dir=dist
npm run build
npx cap add android
npx cap sync android

REM ---- 6. 改 gradle-wrapper.properties 的 distributionUrl(见 5.1) ----
cd /d D:\dev\projects\hello-app\android
gradlew.bat assembleDebug --no-daemon
dir /s /b app\build\outputs\apk\*.apk

REM ---- 7. release 签名(见 7.2--7.5,含手工编辑) ----
gradlew.bat assembleRelease --no-daemon
D:\dev\Android\Sdk\build-tools\36.0.0\apksigner.bat verify --verbose --print-certs app\build\outputs\apk\release\app-release.apk

10.4 已知限制

  • debug 包带本机 debug 签名,可直接安装、不可上架;换电脑重打包覆盖安装会签名冲突。
  • release 包需 keystore + signingConfigs,未签名产物装不上;keystore 丢失无法给老用户升级。
  • 商店上架要 .aab(bundleRelease),不是 APK。
  • iOS 无法在 Windows 编译,需 macOS 或云构建。
  • 未连真机时无法验证实际显示效果(白屏与否),apksigner verify 只能确认签名有效。
  • 项目路径须纯英文;中文路径需 android.overridePathCheck=true 兜底。
  • 本文档不记录任何真实密码;keystore 与密码请自行异地备份。
相关推荐
阿杰m4 小时前
Android源码第十三章:Bluetooth——沿一次发现走到控制器
android
booboosui4 小时前
派活给另一个 AI:前后两次文件夹快照,就是交付清单
android
普通人610 小时前
MySQL_FIND_IN_SET函数详解
android·数据库·mysql
好的佩奇21 小时前
Dart 之集合元素
android·flutter·dart
Easyhood1 天前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
FlightYe1 天前
音视频修炼之容器与协议(四):RTSP详解
android·linux·ffmpeg·音视频·dash
疯华整猫1 天前
删掉 LeakCanary 那天,我用 Trae 自己撸了一套:最难的不是分析堆,是决定什么时候 dump
android·人工智能·性能优化
mmsx1 天前
耳切法切带洞多边形:论文能跑通,真实数据为什么崩
android·kotlin
洞见前行1 天前
你的 Activity 关了,为什么还活着?Android 内存泄漏从发现、定位到线上治理
android·性能优化·leakcanary