在 Expo Updates 里换台:如何在运行时切换更新频道

通过 Expo 的 OTA Update Channel Surfing,你可以即时把变更推送给特定用户。现在无需重新安装,就能在运行时切换更新通道。

中文
复制
Channel surfing for Expo Updates: How to switch update channels at runtime

你是否遇到过这种情况:想把某个改动直接推送给某位特定用户,让他们立刻试用并给出反馈,却不必请他们从 TestFlight 下载并安装新的构建版本?我也遇到过。

有些客户提出的改进我能很快实现,但要把结果交到他们手上验证,就意味着要么给所有用户发一次更新(对一个实验性改动来说风险不小,带宽也用得不值),要么专门为这位客户单独打一个构建(双方都麻烦)。

缺的是灵活性。开发者希望能按需向不同用户群体推送不同的更新,比如非技术相关的干系人、QA,或者合适的时候面向全部用户。

过去,生产构建没有任何办法切换到一个开发中的版本、收集反馈,然后再切回生产版本。

“channel surfing”解决的正是这个问题。用户设备上已安装的应用可以在运行时切换更新渠道,让一个生产应用变成可以随时评审和迭代的空间,而不是一个固定的终点。对于使用生产应用的非技术干系人来说,这一点尤其有用:他们可以直接在已经装好的应用里测试改动并给出反馈。

了解更新渠道

更新渠道是 EAS Update 将更新定向到特定构建的方式。每个构建都关联一个渠道,由该渠道决定它会收到哪些更新。

例如,你可以把更新发布到 preview 渠道,而不影响 production 上的用户。过去,切换渠道需要安装另一个原生构建。

如果你不熟悉更新渠道,EAS Update 文档里有更详细的介绍。

什么是 channel surfing?

channel surfing 让已安装的应用无需重新安装就能从不同的更新流中拉取更新。已安装的应用可以在运行时切换更新渠道,之后它会持续从新选中的渠道接收更新,直到应用被卸载或切换到另一个渠道。

实际用起来是这样的:产品负责人或 QA 可以把 production 构建切到比如 preview 渠道,试用最新的改动。测完再切回 production。不需要重装,也不用单独出一个预览构建。

底层实现上,channel surfing 的原理是让 app 告诉 updates 客户端该用哪个渠道。这个选择可以在运行时更改,并且一直生效,直到被清除或替换。

如何实现 channel surfing

要试用 channel surfing,项目需要先配置好 EAS Update。配置方法参见 EAS Update 入门指南

本质上,channel surfing 只靠一个 API 调用驱动:

Updates.setUpdateRequestHeadersOverride({
  "expo-channel-name": "your-channel",
});

它会设置查询 EAS Update 时使用的 channel header。详细了解 setUpdateRequestHeadersOverride API

为了体验更好,通常不会只切换渠道然后等下次 app 重启。常见的做法是立即检查更新,有更新就下载,然后重载 app,让用户直接进入所选渠道的更新版本。

典型流程如下:

切换渠道(setUpdateRequestHeadersOverride

检查更新(checkForUpdateAsync

获取并应用更新(fetchUpdateAsync

重载 app(reloadAsync

把这些串起来的简单示例大致如下:

import * as Updates from "expo-updates";

async function channelSurfAsync(selectedChannel: string) {
    // Set the updates channel
    Updates.setUpdateRequestHeadersOverride({
      "expo-channel-name": selectedChannel,
    });

    // Check if an update is available
    const { isAvailable } = await Updates.checkForUpdateAsync();

    if (isAvailable) {
      // Fetch and install the update
      await Updates.fetchUpdateAsync();
    }

    // Reload the app
    await Updates.reloadAsync({
      reloadScreenOptions: {
        backgroundColor: "#F8FAFC",
        spinner: {
          enabled: true,
          size: "large",
        },
        fade: true,
      },
    });
  }

这个流程怎么组织由你决定。可以把这些步骤拆到多次交互里,也可以一次全部执行。无论怎么组织,都要考虑失败的情况。比如网络问题或渠道无效,都会导致更新无法应用。

channel surfing 通常只对一部分用户开放,而不是 app 的所有用户。比如你可以只给已登录的员工显示一个按钮,点击后把 app 切到 preview 渠道。可用渠道列表需要在 app 里定义。目前没有公开的 HTTP API 可以获取已有的更新渠道(已在路线图中),所以渠道切换界面只能基于一组已知的渠道名,或者你自己搭一个端点去调用 eas channel:list

设置好 channel override 之后,该设备上后续所有与更新相关的操作,EAS Updates API 都会使用这个 channel,直到应用被卸载,或者这个 override 本身被覆盖。

顺带一提,SDK 54 中的 reloadAsync 对重载体验提供了更多控制。现在可以自定义应用重载时显示的 UI,避免了过去那种短暂闪出空白内容的情况。应用更新这件事因此显得更有章法,也更顺滑。

如何测试 channel surfing 是否生效

要真正看到 channel surfing 的效果,你需要一个 release build——expo-updates API 的大部分功能只在 release build 中可用(除非你在构建时启用了 EX_UPDATES_NATIVE_DEBUG)。在 debug build 中,应用通常改为从开发服务器加载 JavaScript,这会绕过正常的更新流程。

为了方便,我们可以用 EAS Build 创建一个 release build 来测试。如果你还没配置过 EAS Build,请参考 EAS Build 入门指南

在 EAS Build 中,除非你显式选择 development build(例如使用 developmentClient: true),或者显式覆盖原生构建配置,否则构建默认使用 release 配置。在这个 profile 里我们不改动 release 配置,只改输出格式:iOS 模拟器应用(ios.simulator: true)和可直接安装的 Android APK(android.buildType: "apk"),方便在本地测试。

{
  "cli": {
    "version": ">= 16.28.0",
    "appVersionSource": "remote"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "channel": "development"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "autoIncrement": true,
      "channel": "production"
    },
+   "production-simulator": {
+     "channel": "production",
+     "ios": {
+       "simulator": true
+     },
+     "android": {
+       "buildType": "apk"
+     }
+   }
  },
  "submit": {
    "production": {}
  }
}

然后构建项目。

eas build --profile production-simulator --platform [ios/android]

构建完成后,下载并安装到你的模拟器或真机上。

发布更新并切换 channel

应用安装好之后,向另一个 channel 发布一次更新:

eas update --channel preview

接着在应用里进入你的 channel surfing UI,触发 channel 切换。比如,你可以放几个按钮,在 productionpreview 两个 channel 之间切换。触发切换后,应用应该会从选中的 channel 拉取更新,并重载到新的更新上。

Expo OTA 更新的坑

下面这些都不是 channel surfing 特有的问题,但一旦你开始在运行时切换 channel,它们很快就会暴露出来。

运行时版本不匹配

如果更新的运行时版本与已安装应用的运行时版本不一致,该更新不会被下载或应用。在 channel surfing 时,这通常表现为应用切换了 channel,但没有任何更新被应用,即使该 channel 上确实存在更新。

这通常意味着该更新是从另一个原生版本的应用发布的。

移除或撤销更新

另一个常被忽视的行为是更新如何被移除或撤销。如果应用已经为某个 channel 下载了更新,从 EAS dashboard 中删除该更新(或用 eas update:delete)并不会把它从已经拥有该更新的设备上移除。删除只会阻止未来的下载。

撤销一个坏更新最可靠的方式是重新发布一个已知良好的更新到同一个 channel。这会在该 channel 历史记录的顶部创建一个新更新,客户端会将其视为最新版本并应用它。

另一种方式是,EAS Update 提供了回滚机制(eas update:rollback),可以指示客户端重新应用之前的某个稳定更新,或回退到构建中内嵌的更新。

为什么 channel surfing 能改善移动端迭代

当你需要在生产环境中快速审查改动时,channel surfing 尤其有用。

例如,假设有一个紧急 bug 修复需要在广泛推送之前得到验证。借助 channel surfing,可以把这项改动隔离给一小群指定用户,让他们在它进入生产环境之前进行审查。

产品负责人或 QA 可以把已安装的生产构建切换到另一个更新 channel,验证该修复或功能,完成后再切回来。

这让非技术相关方更容易参与审查和决策,同时保持工作流程顺畅。一个生产构建就此成为用于测试、反馈和验证的灵活工具。

切换 channel 的风险与注意事项

切换 channel 会改变应用运行的 JavaScript bundle。如果你的应用依赖跨 channel 不兼容的迁移或数据形态,来回切换可能会引发问题。

例如,如果 beta 更新执行了数据库迁移,生产版本可能无法理解新的 schema。开发者应确保更新在切换时保持安全,或在需要时限制为只能单向切换。

资源

来源: Expo Blog← 返回首页