Flutter 签名与打包:从 keystore 到应用商店

《Flutter 签名与打包:从 keystore 到应用商店》

先说边界

应用开发到上架的最后一公里:生成签名材料、配好 Android 的 release 签名、用一条命令出 aab/apk、走 Play Console 的上架主干,iOS 证书与 TestFlight 一段带过,CI 给骨架。边界先说死:iOS 的证书体系(App ID、描述文件、推送配置)在 Mac 加 Xcode 里才讲得清,本文只给主干和最常见报错;代码混淆与加固不展开(Gradle 的 R8 收缩默认已开);多渠道打包(flavor)是另一个话题;商店审核政策细节以官方文档为准。版本口径:Flutter 3.24(2024-08-06 GA,配套 Dart 3.5),Android 模板在这个版本仍是 Groovy 构建脚本(build.gradle)。产出是一条可重复的出包命令链加一份上架主干清单。

版本号从哪来

Flutter 把双端版本号收进 pubspec.yaml 一行:

version: 2.1.0+17

加号前是 versionName(用户看见的 2.1.0),加号后是 versionCode(商店认的 17)。规则只有一条:versionCode 只增不减,商店拿它判断”这是不是新版本”。构建时可覆盖:flutter build appbundle --build-number=18——CI 里最常见的就是把流水线序号喂给它,人不用记今天该填几。心智要分开:name 随便起,code 是一本严格递增的账。最容易犯的错是”发一版忘改一版”:pubspec 不动、CI 也不喂 build-number,第二次上传被拒才想起来。我的做法是发布类构建永远带 –build-number,本地出包才允许用 pubspec 默认值——两套来源别混用。

生成签名材料

签名材料就一个密钥库文件加一份密码。keytool 随 JDK 自带(flutter doctor -v 里能看到 Java 路径):

keytool -genkey -v -keystore ~/upload-keystore.jks 
        -keyalg RSA -keysize 2048 -validity 10000 -alias upload

Windows 把路径换成 %USERPROFILE%upload-keystore.jks。两个做完就不许反悔的决定:有效期——10000 天是官方示例值,够长,但商店密钥的生命周期是”应用活多久它活多久”;备份——文件加密码离线存好,丢了没有任何找回渠道(启用 Play App Signing 的话上传密钥可重置,但别把希望押在这个流程上)。

接着写配置文件 android/key.properties(此文件不进版本库):

storePassword=你的库密码
keyPassword=你的钥匙密码
keyAlias=upload
storeFile=/Users/you/upload-keystore.jks

storeFile 建议指向版本库外的绝对路径(官方部署文档的推荐做法),keystore 与 key.properties 都进 .gitignore——签名材料进 git 仓库是真实发生过的安全事故类别。

把签名接进构建

android/app/build.gradle 里读配置、建 signingConfig、挂到 release:

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

这段是官方部署文档的骨架,逐字可复制。exists() 判断是给 CI 留的活口:本地有 key.properties 就签名,没有(比如 PR 构建)就落回 debug 签名,流水线不至于直接红。配置完做个冒烟:flutter build apk --debug 与 flutter build apk --release 各跑一遍,release 不再报 signingConfig 缺失就算接通。

出包:aab 与 apk 各走各路

# 上架 Play:应用包
flutter build appbundle --release
1. 产物:build/app/outputs/bundle/release/app-release.aab

1. 侧载 / 国内商店:分包 APK
flutter build apk --split-per-abi
1. 产物:build/app/outputs/flutter-apk/app-arm64-v8a-release.apk 等

选型不纠结:Play 商店 2021-08 起新应用强制 aab(公开口径),商店按设备动态下发,包体最小;国内商店或直接发安装包,用 –split-per-abi 的 apk,按 ABI 分包后每个包都比 fat apk 小一截。aab 包体小的原理也值得知道:它不是”压缩更狠”,是把”这台设备需要哪些资源与 so”的拆分计算交给商店——同一份 aab 装到不同设备,落地的安装包并不一样。出完包验一次签名(apksigner 在 Android SDK 的 build-tools 里):

apksigner verify --print-certs build/app/outputs/flutter-apk/app-arm64-v8a-release.apk

打印出的别名是 upload、指纹与本地密钥库一致,签名链就贯通了。release 包务必真机装一遍冒烟:图标、启动、核心流程。debug 手感好不代表 release 没问题——release 的裁剪与混淆偶尔会碰掉反射式的代码,插件在 release 下行为异常时,先怀疑这里再查代码。

Play Console 上架主干

开发者账号一次性 25 美元(公开口径)。流程主干:

  • 创建应用,填商店信息:名称、描述、截图、512 图标——素材可后补,提审前必须齐;截图与文案后台随时能迭代,先把隐私政策和数据安全表单做对,这两样改起来最折腾;
  • 应用内容问卷:隐私政策 URL、数据安全表单、内容分级、目标受众——数据安全表单要与代码里实际申请的权限对得上,”表单说不用收集、代码里却在要定位”是常见驳回原因;
  • 先走内部测试轨道:传 aab、拉几个测试邮箱,通常很快能装上(注意一条政策:2023 年底后新注册的个人开发者账号,要先跑一段封闭测试、满足人数与时长要求才能开放生产轨道——以官方当前政策为准);
  • 生产提审:首次审核常以天计,之后的更新一般更快——定性口径,别拿别人的一次经验当 SLA。

Play App Signing 会在上传第一个 aab 时引导启用:Google 保管应用签名密钥,你手里的 upload key 只负责上传——这也是上传密钥丢失后能重置的原因。每个轨道都要求新上传的 versionCode 大于该轨道已有版本,那本”只增不减”的账在这里兑现。

iOS:一段带过

主干一句话:flutter build ipa 出 archive(build/ios/archive/Runner.xcarchive)与 ipa,需要 Apple Developer Program(99 美元/年,公开口径);Xcode 的 Signing & Capabilities 配签名,个人团队自动签名最省事,团队协作建议手动证书加描述文件;需要指定导出方式时给 --export-options-plist 传一份 ExportOptions.plist(method 选 app-store-connect),细节以 flutter build ipa -h 的输出为准;ipa 经 Transporter 或 Xcode Organizer 上传 App Store Connect;TestFlight 分内部测试(上限 100 人,基本即时可用)与外部测试(需一次轻量审核,可容纳上万名测试者);最后在 App Store Connect 提审。最常见的报错是证书或描述文件过期——Xcode 报 signing error 先查这个,再查 Bundle ID 是否唯一。

CI 一段

GitHub Actions 的骨架(关键步骤节选,action 版本以各仓库 README 为准):

# .github/workflows/release.yml 关键步骤
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
  with: { distribution: 'temurin', java-version: '17' }
- uses: subosito/flutter-action@v2
  with: { flutter-version: '3.24.0' }
- run: flutter pub get
- run: flutter test
- run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > "${{ runner.temp }}/upload-keystore.jks"
- run: |
    printf 'storePassword=%snkeyPassword=%snkeyAlias=uploadnstoreFile=%sn' 
      "${{ secrets.STORE_PASSWORD }}" "${{ secrets.KEY_PASSWORD }}" 
      "${{ runner.temp }}/upload-keystore.jks" > android/key.properties
- run: flutter build appbundle --build-number ${{ github.run_number }}

思路两条:签名材料从 secrets 还原到临时目录,用完即弃;github.run_number 天然单调递增,正好当 build-number。产物(aab)用 actions/upload-artifact 存档,或推商店的发布 API——商店自动发布链路水更深,先把”绿色按钮出正确签名包”这一步做扎实。依赖下载可以用 actions/cache 按 pubspec.lock 缓存 pub 目录,构建提速是可感的(定性口径),加不加看流水线时长焦虑程度。

常见翻车现场

几个高频事故,先认识再绕行:

  • versionCode 没涨,上传直接被拒——Play 的报错会明说版本号必须大于已有值。回到 pubspec 或交给 –build-number,别手工猜;
  • 密码写死进 build.gradle 还提交了——key.properties 加 .gitignore 从第一天就做对;已经提交过的,改密钥库密码是重活,历史清理更重;
  • release 包闪退而 debug 正常——先用 flutter run --release 真机复现,再怀疑混淆与裁剪碰了插件,最后查插件是否支持 64 位(老插件的高频坑);
  • CI 上 keystore 路径不对——storeFile 用 ${{ runner.temp }} 拼绝对路径,别用相对路径猜工作目录;
  • 内测装包后秒退——adb logcat 或 flutter logs 抓第一现场,栈顶信息比盲猜快十倍。

验收清单

  • git status 干净:keystore 与 key.properties 都不在版本库里;
  • 本地 flutter build appbundle 产出 aab,apksigner 打印的证书别名是 upload;
  • 改 pubspec 的 version 后重出包,versionName/versionCode 与预期一致;
  • 内部测试轨道的 aab 上传成功,测试邮箱能装包且应用能启动;
  • CI 全绿且产物可下载,无签名材料的 PR 构建不因缺 keystore 而红;
  • (可选)TestFlight 收到构建并能在真机安装。

学完之后

发布流水线接在功能开发之后:最后一公里里如果还有”功能要碰原生”的部分,看《Flutter 平台通道:与原生代码握手》;整棵技能树的位置见《Flutter 学习路线》的收尾阶段——路线的最后一站,就是把今天这条流水线亲手跑通一次。

本文数据核对时间:2024-08-12:Flutter 3.24(2024-08-06 GA,配套 Dart 3.5),Android 模板该版本仍为 Groovy(build.gradle);签名与部署步骤以 docs.flutter.dev/deployment/android(2024-08 口径)为骨架;Play 商店 aab 强制(2021-08 起新应用)与 Play App Signing 为公开口径;账号费用(Play 25 美元一次性、Apple Developer Program 99 美元/年)与 TestFlight 内部测试上限 100 人为公开口径;GitHub Actions 的 action 版本以各仓库 README 为准。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇