在同一设备上安装应用变体

了解如何在同一设备上安装应用的多个变体。


在创建开发、预览和生产版本时,通常会在同一设备上同时安装这些构建版本。这允许在设备上进行开发、预览应用的下一个版本,并运行生产版本,而无需卸载和重新安装应用。

🌐 When creating development, preview, and production builds, installing these build variants simultaneously on the same device is common. This allows working in development, previewing the next version of the app, and running the production version on a device without needing to uninstall and reinstall the app.

本指南提供了配置多个(开发和生产)变体以在同一设备上安装和使用它们所需的步骤。

🌐 This guide provides the steps required to configure multiple (development and production) variants to install and use them on the same device.

先决条件

🌐 Prerequisites

要在你的设备上安装应用的多个版本,每个版本必须拥有唯一的 应用 ID (Android)打包标识符 (iOS)

🌐 To have multiple variants of an app installed on your device, each variant must have a unique Application ID (Android) or Bundle Identifier (iOS).

配置开发和生产变体

🌐 Configure development and production variants

你已经使用 Expo 工具创建了一个项目,现在你想创建一个开发版本和一个生产版本。你项目的 app.json 可能有以下配置:

🌐 You have created a project using Expo tooling, and now you want to create a development and a production build. Your project's app.json may have the following configuration:

app.json
{ "expo": { "name": "MyApp", "slug": "my-app", "ios": { "bundleIdentifier": "com.myapp" }, "android": { "package": "com.myapp" } } }

如果你的项目已配置 EAS Build,eas.json 也有如下类似的配置:

🌐 If your project has EAS Build configured, the eas.json also has a similar configuration as shown below:

eas.json
{ "build": { "development": { "developmentClient": true }, "production": {} } }

将 app.json 转换为 app.config.js

🌐 Convert app.json to app.config.js

要在同一设备上安装应用的多个版本,请将 app.json 重命名为 app.config.js,并按如下所示导出配置:

🌐 To have multiple variants of the app installed on the same device, rename the app.json to app.config.js and export the configuration as shown below:

app.config.js
export default { name: 'MyApp', slug: 'my-app', ios: { bundleIdentifier: 'com.myapp', }, android: { package: 'com.myapp', }, };

app.config.js 中,添加一个名为 IS_DEV 的环境变量,用于根据该变量切换每个版本的 android.packageios.bundleIdentifier

🌐 In app.config.js, add an environment variable called IS_DEV to switch the android.package and ios.bundleIdentifier for each variant based on the variable:

app.config.js
const IS_DEV = process.env.APP_VARIANT === 'development'; export default { name: IS_DEV ? 'MyApp (Dev)' : 'MyApp', slug: 'my-app', ios: { bundleIdentifier: IS_DEV ? 'com.myapp.dev' : 'com.myapp', }, android: { package: IS_DEV ? 'com.myapp.dev' : 'com.myapp', } };

在上述示例中,环境变量 IS_DEV 用于区分开发环境和生产环境。根据其值,为每个版本设置不同的应用 ID 或打包标识符。

🌐 In the above example, the environment variable IS_DEV is used to differentiate between the development and production environment. Based on its value, the different Application IDs or Bundle Identifiers are set for each variant.

附加应用变体自定义

你可以针对每个变体自定义应用的其他方面。你可以使用上面相同的方法,替换之前在 app.json 中使用的任何配置。

🌐 You can customize other aspects of your app on a per-variant basis. You can swap any configuration that you used previously in app.json using the same approach as above.

示例:

  • 如果你使用的库需要你向外部服务注册你的应用标识符以使用其 SDK,例如 Google 地图或 Firebase 云消息传递(FCM),那么你需要为 android.packageios.bundleIdentifier 为该 API 配置单独的设置。
  • 如果你正在使用开发版本,可以配置 expo-dev-client 插件以在非开发版本中禁用 Expo CLI 和 EAS Update QR 码使用的应用方案。这可以确保这些 URL 始终启动开发版本,而不受设备默认设置的影响:
app.config.js
plugins: [ [ 'expo-dev-client', { addGeneratedScheme: !!IS_DEV, }, ], ],

EAS 构建配置

🌐 Configuration for EAS Build

eas.json 中,通过使用 env 属性,将 APP_VARIANT 环境变量设置为使用 development 配置运行构建:

🌐 In eas.json, set the APP_VARIANT environment variable to run builds with the development profile by using the env property:

eas.json
{ "build": { "development": { "developmentClient": true, "env": { "APP_VARIANT": "development" } }, "production": {} } }

现在,当你运行 eas build --profile development 时,环境变量 APP_VARIANT 会在本地和 EAS Build 构建器上评估 app.config.js 时被设置为 development

🌐 Now, when you run eas build --profile development, the environment variable APP_VARIANT is set to development when evaluating app.config.js both locally and on the EAS Build builder.

使用开发服务器

🌐 Using the development server

当你启动开发服务器时,你需要运行 APP_VARIANT=development npx expo start(如果你使用 Windows,则运行对应平台的命令)。

🌐 When you start your development server, you'll need to run APP_VARIANT=development npx expo start (or the platform equivalent if you use Windows).

一个快捷方法是在你的 package.json 中添加以下脚本:

🌐 A shortcut for this is to add the following script to your package.json:

package.json
{ "scripts": { "dev": "APP_VARIANT=development npx expo start" } }

使用生产版本

🌐 Using production variant

当你运行 eas build --profile production 时,APP_VARIANT 变量环境未设置,并且构建将以生产版本运行。

🌐 When you run eas build --profile production the APP_VARIANT variable environment is not set, and the build runs as the production variant.

注意:如果你使用 EAS Update 发布应用的 JavaScript 更新,运行 eas update 命令时应谨慎设置所发布应用变体的正确环境变量。有关详细信息,请参阅 EAS Build 的 环境变量和秘密

在一个现有的(空白的)React Native 项目中

🌐 In an existing (bare) React Native project

信息 如果你想使用你的 应用配置文件 作为应用配置的权威来源(包括各个变体的 bundle 标识符和包名),你需要迁移到 连续原生生成 (CNG),并将 androidios 目录添加到你的 .gitignore 文件中。如果你是从 React Native CLI 项目开始并添加了自定义原生代码,这一点尤为重要。这可以确保原生项目配置不会覆盖你的应用配置,尤其是在使用 bundle ID 查找行为时。如果不这样做,你可能会遇到运行错误的应用变体或无法正确检测开发构建的问题。或者,你也可以按照下面描述的方式,明确选择 Android flavors 和 iOS schemes。

安卓

🌐 Android

android/app/build.gradle 中,为你想要构建的 eas.json 中的每个构建配置创建一个单独的 flavor。

🌐 In android/app/build.gradle, create a separate flavor for every build profile from eas.json that you want to build.

android/app/build.gradle
android { %%placeholder-start%%... %%placeholder-end%% flavorDimensions "env" productFlavors { production { dimension "env" applicationId 'com.myapp' } development { dimension "env" applicationId 'com.myapp.dev' } } %%placeholder-start%%... %%placeholder-end%% }

注意:目前,EAS CLI 仅支持 applicationId 字段。如果你在 productFlavorsbuildTypes 部分使用 applicationIdSuffix,该值将无法被正确检测。

通过在 eas.json 中指定 gradleCommand 将 Android 版本分配给 EAS Build 配置文件:

🌐 Assign Android flavors to EAS Build profiles by specifying a gradleCommand in the eas.json:

eas.json
{ "build": { "development": { "android": { "gradleCommand": ":app:assembleDevelopmentDebug" } }, "production": { "android": { "gradleCommand": ":app:bundleProductionRelease" } } } }

默认情况下,每种风味都可以以调试模式或发布模式构建。如果你想将某些风味限制为特定模式,请参见下面的代码片段,并修改 build.gradle

🌐 By default, every flavor can be built in either debug or release mode. If you want to restrict some flavor to a specific mode, see the snippet below, and modify build.gradle.

android/app/build.gradle
android { %%placeholder-start%%... %%placeholder-end%% variantFilter { variant -> def validVariants = [ ["production", "release"], ["development", "debug"], ] def buildTypeName = variant.buildType*.name def flavorName = variant.flavors*.name def isValid = validVariants.any { flavorName.contains(it[0]) && buildTypeName.contains(it[1]) } if (!isValid) { setIgnore(true) } } %%placeholder-start%%... %%placeholder-end%% }

此时其余的配置并不是特定于 EAS 的,它与任何具有版本风格的 Android 项目相同。有一些常用的配置,你可能希望应用到你的项目中:

🌐 The rest of the configuration at this point is not specific to EAS, it's the same as it would be for any Android project with flavors. There are a few common configurations that you might want to apply to your project:

  • 要更改使用开发配置文件构建的应用名称,请创建一个 android/app/src/development/res/value/strings.xml 文件:

    android/app/src/development/res/value/strings.xml
    <resources> <string name="app_name">MyApp - Dev</string> </resources>
  • 要更改使用开发配置文件构建的应用的图标,请创建 android/app/src/development/res/mipmap-* 目录并放置相应的资源(你可以从 android/app/src/main/res 复制它们并替换图标文件)。

  • 要为特定的 flavor 指定 google-services.json,请将其放在 android/app/src/{flavor}/google-services.json 文件中。

  • 要配置 Sentry,在 android/app/build.gradle 中添加“project.ext.sentryCli = [ flavorAware: true ]”,并将属性文件命名为 android/sentry-{flavor}-{buildType}.properties(例如,android/sentry-production-release.properties

iOS

eas.json 中为每个构建配置分配不同的 scheme

🌐 Assign a different scheme to every build profile in eas.json:

eas.json
{ "build": { "development": { "ios": { "buildConfiguration": "Debug", "scheme": "myapp-dev" } }, "production": { "ios": { "buildConfiguration": "Release", "scheme": "myapp" } } } }

Podfile 应该像这样定义一个 target:

Podfile
target 'myapp' do %%placeholder-start%%... %%placeholder-end%% end

将其替换为抽象目标,可以从旧目标复制通用配置:

🌐 Replace it with an abstract target, where the common configuration can be copied from the old target:

Podfile
abstract_target 'common' do # put common target configuration here target 'myapp' do end target 'myapp-dev' do end end

在 Xcode 中打开项目,在导航面板中点击项目名称,右键点击现有的目标,然后点击“复制”。

🌐 Open project in Xcode, click on the project name in the navigation panel, right click on the existing target, and click "Duplicate":

将目标重命名为更有意义的名称,例如,myapp copy -> myapp-dev

🌐 Rename the target to something more meaningful, for example, myapp copy -> myapp-dev.

为新目标配置方案:

🌐 Configure a scheme for the new target:

  • 前往 Product -> Scheme -> Manage schemes
  • 在列表中找到方案 myapp copy
  • 将方案名称 myapp copy 更改为 myapp-dev
  • 默认情况下,新方案应标记为共享,但 Xcode 不会创建 .xcscheme 文件。要解决此问题,请取消选中“共享”复选框,然后再次勾选,之后新的 .xcscheme 文件应出现在 ios/myapp.xcodeproj/xcshareddata/xcschemes 目录中。

默认情况下,新创建的目标有一个单独的 Info.plist 文件(在上面的示例中,它是 ios/myapp copy-Info.plist)。为了简化项目,我们建议所有目标使用相同的文件:

🌐 By default, the newly created target has separate Info.plist file (in the above example, it's ios/myapp copy-Info.plist). To simplify your project we recommend using the same file for all targets:

  • 删除 ./ios/myapp copy-Info.plist
  • 点击新的目标。
  • 转到 Build Settings 选项卡。
  • 找到 Packaging 部分。
  • 更改 Info.plist 的值 - myapp copy-Info.plist -> myapp/Info.plist
  • 更改 Product Bundle Identifier

要更改显示名称:

🌐 To change the display name:

  • 打开 Info.plist 并添加键 Bundle display name,值为 $(DISPLAY_NAME)
  • 打开 Build Settings 对两个目标,并找到 User-Defined 部分。
  • 添加键 DISPLAY_NAME,并使用你想为该目标使用的名称。

要更改应用图标:

🌐 To change the app icon:

  • 创建一个新的图片集(你可以从当前图标的现有图片集中创建,它通常命名为 AppIcon
  • 打开 Build Settings 以更改目标的图标。
  • 找到 Asset Catalog Compiler - Options 部分。
  • Primary App Icon Set Name 更改为新图片集的名称。