• 打包和发布到 Android 平台
    • 添加启动图标
    • 为 app 签名
      • 创建一个密钥库
      • 从 app 中引用密钥库
      • 在 gradle 中配置签名
    • 启用混淆器
      • 步骤 1 - 配置 Proguard
      • 步骤 2 - 启用混淆以及/或压缩
    • 检查 app manifest 文件
    • 检查构建配置
    • 为发布构建应用程序
      • 构建一个 app bundle
      • 测试 app bundle
        • 离线使用 bundle tool
        • 在线使用 Google Play
      • 构建一个 APK
      • 在设备上安装 APK 文件
    • 发布到 Google Play Store
    • 更新应用版本号
    • Android发布常见问题
      • 我应该什么时候构建 app bundles 而不是 APKs?
      • 什么是 fat APK?
      • 哪些目标架构是被支持的?
      • 如何为一个使用 flutter build appbundle 创建的 app bundle 签名?
      • 如何使用 Android Studio 构建一个发布?

    打包和发布到 Android 平台

    在一般的开发过程中,我们可以使用 flutter run 命令,或者 IntelliJ 工具栏中的 RunDebug 来测试 app。这时候,Flutter 默认会为我们构建 app 的调试版本。

    当想要发布 app 时,比如 发布到 Google Play Store,可以按照以下步骤来准备 Android 平台的 发布 版本。本页面的内容包含如下主题:

    • 添加启动图标

    • 创建一个密钥库

    • 启用混淆器

    • 检查 app manifest 文件

    • 检查构建配置

    • 为发布构建应用程序

    • 发布到 Google Play Store

    • 更新应用版本号

    • 安卓发布常见问题

    添加启动图标

    当我们创建一个新的 Flutter app 的时候,它会有一个默认的启动图标。要自定义这个图标,可以参考[Flutter Launcher Icons][]。

    或者,如果我们想手动操作,可以参考以下方法:

    • 查看 Material Design Product Icons 指南中图标设计部分。

    • <app dir>/android/app/src/main/res/ 目录下,把我们的图标文件放在以 配置限定符 命名的文件夹中。类似默认的 mipmap- 文件夹这样的命名方式。

    • AndroidManifest.xml 中,更新 application 标签中的 android:icon属性来引用上一步骤中我们自己的图标文件(例如,<application android:icon="@mipmap/ic_launcher" …)。

    • flutter run 运行 app,检查启动程序中的 app 图标是否已经替换成我们自己的图标文件。

    为 app 签名

    要想把 app 发布到 Play store,还需要给 app 一个数字签名。我们可以采用以下步骤来为 app 签名:

    创建一个密钥库

    如果我们已经有一个密钥库,可以跳到下一步。如果没有,在命令行中运行以下的命令来创建一个:

    在 macOS 或者 Linux 系统上,执行下面的代码

    1. ```terminal
    2. keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

    On Windows, use the following command:

    1. keytool -genkey -v -keystore c:/Users/USER_NAME/key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key

    备忘

    保证这个文件的私有性,不要将它提交到公共的代码管理空间。

    备忘

    • keytool 可能不在我们的系统路径中。它是 Java 的一部分,在安装 Android Studio 的时候会被一起安装。运行 flutter doctor -v,’Java binary at:’ 之后打印出来的就是它的路径,然后用 java 来替换以上命令中的 keytool,并加上 keytool 的完整路径即可。如果文件路径包含空格,类似 Program Files 这样的,你需要在路径上加入转义符:/"Program Files"/

    • 只有 Java 9 或更高版本才需要 -storetype JKS 标签。从 Java 9 版本开始,keystore 类型默认为 PKS12。

    从 app 中引用密钥库

    创建一个名为 <app dir>/android/key.properties 的文件,它包含了密钥库位置的定义:

    1. storePassword=<上一步骤中的密码>
    2. keyPassword=<上一步骤中的密码>
    3. keyAlias=key
    4. storeFile=<密钥库的位置,e.g. /Users/<用户名>/key.jks>

    备忘

    (再次)请保证这个文件的私有性,不要将它提交到公共的代码管理空间。

    在 gradle 中配置签名

    通过编辑 <app dir>/android/app/build.gradle 文件来为我们的 app 配置签名:

    • 将如下内容:
    1. android {

    替换为我们的 properties 文件的密钥库信息:

    1. def keystoreProperties = new Properties()
    2. def keystorePropertiesFile = rootProject.file('key.properties')
    3. if (keystorePropertiesFile.exists()) {
    4. keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    5. }
    6. android {
    • 将如下内容:
    1. buildTypes {
    2. release {
    3. // TODO: Add your own signing config for the release build.
    4. // Signing with the debug keys for now,
    5. // so `flutter run --release` works.
    6. signingConfig signingConfigs.debug
    7. }
    8. }

    替换为我们的配置内容:

    1. signingConfigs {
    2. release {
    3. keyAlias keystoreProperties['keyAlias']
    4. keyPassword keystoreProperties['keyPassword']
    5. storeFile file(keystoreProperties['storeFile'])
    6. storePassword keystoreProperties['storePassword']
    7. }
    8. }
    9. buildTypes {
    10. release {
    11. signingConfig signingConfigs.release
    12. }
    13. }

    现在我们 app 的发布版本就会被自动签名了。

    启用混淆器

    默认情况下,Flutter 不会做混淆或者压缩 Android host 的工作。如果 app 使用了第三方的 Java 或者 Android 库,我们会希望减小 APK 的大小,或者保护代码不被反编译出来。

    要了解混淆 Dart 代码的相关信息,可以参考 Flutter wiki上的 Obfuscating Dart Code。

    步骤 1 - 配置 Proguard

    创建 /android/app/proguard-rules.pro 文件并添加下面的规则:

    1. ## Flutter wrapper
    2. -keep class io.flutter.app.** { *; }
    3. -keep class io.flutter.plugin.** { *; }
    4. -keep class io.flutter.util.** { *; }
    5. -keep class io.flutter.view.** { *; }
    6. -keep class io.flutter.** { *; }
    7. -keep class io.flutter.plugins.** { *; }
    8. -dontwarn io.flutter.embedding.**

    以上这样的配置只是对 Flutter 引擎库做保护。如果想要保护其他的库(例如,Firebase),需要为它们添加自己的规则。

    步骤 2 - 启用混淆以及/或压缩

    /android/app/build.gradle 文件找到 buildTypes 的定义。在 release 配置中设置 minifiyEnableduseProguard 为 true。另外我们必须再设置 Proguard 指向步骤 1 中我们创建的文件。

    1. android {
    2. ...
    3. buildTypes {
    4. release {
    5. signingConfig signingConfigs.release
    6. minifyEnabled true
    7. useProguard true
    8. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    9. }
    10. }
    11. }

    备忘

    混淆和压缩会大大地延长安卓应用程序的编译时间。

    检查 app manifest 文件

    检查位于 <app dir>/android/app/src/main 的默认 App Manifest 文件 AndroidManifest.xml,并确认各个值都设置正确,特别是:

    • application
    • Edit the android:label in theapplication tag to reflect the final name of the app.application:编辑 application标签中的 android:label 来设置 app 的最终名字。

    • uses-permission

    • Add the android.permission.INTERNETpermission if your application code needs Internetaccess. The standard template does not include this tag but allowsInternet access during development to enable communication betweenFlutter tools and a running app.uses-permission:如果你的代码需要互联网交互,请加入android.permission.INTERNET 权限标签。标准开发模版里并未加入这个权限(但是 Flutter debug 模版加入了这个权限),加入这个权限是为了允许 Flutter 工具和正在运行的 app 之间的通信,详情见 Issue 22139。

    检查构建配置

    检查位于 <app dir>/android/app 的默认 Gradle build file,并确认各个值都设置正确,特别是下面 defaultConfig 块中的值:

    • applicationId
    • Specify the final, unique (Application Id)appid

    applicationId:指定最终的,唯一的(Application Id)appid。

    • versionCode & versionName
    • Specify the internal app version number,and the version number display string. You can do this by settingthe version property in the pubspec.yaml file. Consult the versioninformation guidance in the versions documentation.

    versionCode & versionName:指定 app 的内部版本号,以及用于显示的版本号,这可以通过设置 pubspec.yaml 文件中 version 属性来做。具体可以参考 版本文档 中的版本信息指南。

    • minSdkVersion & targetSdkVersion
    • Specify the minimum API level,and the API level on which the app is designed to run.Consult the API level section in the versions documentationfor details.

    minSdkVersion & targetSdkVersion:指定支持的最低 API 版本,以及我们 app 的目标 API 版本。具体可以参考 版本文档 中的 API 版本部分。

    为发布构建应用程序

    当要发布到 Play Store 时,你有两种可能的发布方式

    • App bundle (推荐)

    • APK

    备忘

    Google Play Store 更推荐 app bundle 方式. 更多信息可以参考 Android App Bundle and About Android App Bundles.

    构建一个 app bundle

    这个部分描述了如何构建一个发布的 app bundle。如果在前面的部分已经完成了签名步骤,发布的 bundle 会被签名。

    请注意 Recently, the Flutter team has received several reports from developers indicating they are experiencing app crashes on certain devices on Android 6.0 when building an app bundle.

    最近 Flutter team 收到的几份开发者反馈显示,他们在尝试构建 app bundle 的时候,会在某些 Android 6.0 某些设备上崩溃。

    在 Android team 努力寻找可行的解决方案时,你可以先尝试将 APK 拆分作为临时解决方案。更多有关信息请查看 Issue 36822。

    使用如下命令:

    • 运行 cd <app dir>。(将 <app dir> 替换为我们 app 的目录)。

    • 运行 flutter build appbundle。(运行 flutter build 默认构建一个发布版本。)

    你的应用的 release bundle 会被创建到<app dir>/build/app/outputs/bundle/release/app.aab.

    此 app bundle 会默认地包含为 armeabi-v7a (32-bit) 和 arm64-v8a (64-bit) 编译的 Dart 和 Fluter 运行时代码。

    测试 app bundle

    一个 app bundle 可以用多种方法测试,这里介绍两种。

    离线使用 bundle tool

    • 如果你还没准备好,可以从 GitHub repository 下载 bundletool

    • 从你的 app bundle Generate a set of APKs

    • Deploy the APKs to connected devices.

    Deploy the APKs 连接到你的设备

    在线使用 Google Play

    • 上传你的 bundle 到 Google Play 去测试它。或者在正式发布之前用 alpha 或 beta 频道去测试。

    • 按照 these steps to upload your bundle 上传到 Play Store。

    构建一个 APK

    虽然 app bundle 比 APKs 更被推荐使用,但是有一些 Store 目前还不支持 app bundle方式。这种情况下,要为各种目标 ABI (Application Binary Interface) 分别构建发布的 APK 文件。

    如果你完成签名步骤, APK 就被签名了。

    使用如下命令:

    • cd <app dir> (将 <app dir> 替换为我们 app 的目录)。

    • 运行 flutter build apkflutter build 默认带有 —release 参数)。

    这个命令会生成两个 APK 文件:

    • <app dir>/build/app/outputs/apk/release/app-armeabi-v7a-release.apk
    • <app dir>/build/app/outputs/apk/release/app-arm64-v8a-release.apk

    如果移除 —split-per-abi 将会生成一个包含所有目标 ABI 的 fat APK 文件。这种 APK 文件将会在比单独构建的 APK 文件尺寸要大,会导致用户下载一些不适用于其设备架构的二进制文件。

    在设备上安装 APK 文件

    按照如下这些步骤,将前一步中构建出来的 APK 安装到 Android 设备上。

    使用如下命令:

    • 用 USB 线将 Android 设备连接到电脑上。

    • cd <app dir><app dir> 是我们 app 的目录。

    • 运行 flutter install

    发布到 Google Play Store

    要了解如何发布一个 app 到 Google Play Store,可以参考 Google Play publishing documentation。

    当你创建了应用之后,你可以通过 Google Ads 吸引更多用户,Google Ads 平台可以通过机器学习帮助你以非常高的性价比吸引到更多用户。

    通过以下几步创建一个广告宣传:

    • 创建广告—我们会根据您的应用信息帮您制作广告。另外,您还可以添加图片和视频。

    • 决定推广预算—对于以提高应用安装量为主要目标的广告系列,您需要为其设置应用安装出价,也就是“目标每次安装费用”,同时设置每日推广支出预算。

    • 选择目标地区—让我们知道你希望触达哪些区域的用户。

    • 设定用户行动—决定你希望用户要做什么,比如安装,应用内操作或者目标广告支出回报率 (ROAS)。

    获取 75 美元的赠金(当你消费 25 美金后)

    更新应用版本号

    每个应用默认的初始版本号是 1.0.0。若要更新它,请转到 pubspec.yaml 文件并更新以下内容:

    version: 1.0.0+1

    版本号由三个点分隔的数字组成,例如上面样例中的 1.0.0。然后是可选的构建号,例如上面样例中的 1,以 + 分隔。

    版本号与构建号都可以在 Flutter 打包时分别使用 —build-name—build-number 重新指定。

    在 Android 中,当 build-number 被用作 versionCodebuild-name 作为 versionName 使用。更多信息请参考 Android 文档中的为你的应用添加版本。

    Android发布常见问题

    这里是一些关于安卓应用程序发布的常见问题。

    我应该什么时候构建 app bundles 而不是 APKs?

    Google Play Store 相对于 APKs 更建议你发布 app bundles,因为那样应用程序会更有效率地交付给你的用户。但是,如果你想将应用程序发布到其他的应用商店, APK可能是唯一选项。

    什么是 fat APK?

    一个 fat APK 是一个包含了支持多个 ABI 架构的 APK 文件。这样做的好处是单个 APK 可以运行在多个架构上,因此具有更广泛的兼容性。但同时缺点就是文件体积会比较大,导致用户在安装你的应用程序时会下载和储存更多的字节。当构建 APKs 而不是app bundles 时强烈建议分开构建 APKs,如 build an APK 所描述的那样,使用 —split-per-abi 指令。

    哪些目标架构是被支持的?

    当使用 release 模式构建你的应用程序时, Flutter app 可以基于 armeabi-v7a (32-bit)和 arm64-v8a (64-bit)被编译。Flutter 目前不支持 x86 Android (参考 Issue 9253).

    如何为一个使用 flutter build appbundle 创建的 app bundle 签名?

    See Signing the app.

    如何使用 Android Studio 构建一个发布?

    在Android Studio中, 打开你的 app 文件夹下的 android/文件夹. 然后在项目面板中选择 build.gradle (Module: app) :

    screenshot of gradle build script menu

    接下来,选择构建变体。在主菜单中点击 Build > Select Build Variant。从 Build Variants 面板中选择任意一个变体(默认是 debug)。

    screenshot of build variant menu

    生成的 app bundle 或 APK 文件会在你的 app 所在文件夹下的 build/app/outputs 文件夹下。