EAS 更新调试

编辑页面

了解如何使用基本调试技术来修复更新问题。


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

本指南展示了如何验证我们的配置,以便我们能够找到诸如应用未显示已发布更新之类问题的来源。重要的是在任何给定时间都能说明我们应用的当前状态,因此 EAS Update 正是基于这一点构建的。一旦我们知道哪些更新正在哪些构建上运行,我们就可以进行更改,使我们的应用处于我们期望的状态。

如何调试 EAS Update
如何调试 EAS Update

在这个 Expo 调试教程中,你将学习如何调试构建未获取到更新的情况。


如果我们没有使用 EAS Build,我们的 Deployments 页面将是空的。请改为遵循 不使用 EAS Build 时调试配置的指南

前往 Deployments 页面

EAS 网站有一个 Deployments 页面,用于显示我们应用的当前状态。术语 deployment 指的是一组构建及其对应更新的集合。如果我们使用 EAS 创建了构建和更新,就可以在 Deployments 选项卡中看到我们项目的状态。

常见问题

以下部分描述了常见问题以及如何修复它们。下面是一张 EAS Update 工作方式的示意图,以及在查找问题根因时有用的检查点。在接下来的章节中,我们将检查并验证这些位置以及更多内容。

意外的 channel

如果 deployment 的 channel 不符合预期,这意味着我们的构建不是使用正确的 channel 构建的。要修复此问题,请配置我们的 channel并重新构建应用。

意外的 runtime version

如果 deployment 的 runtime version 不符合预期,这意味着我们的构建不是使用正确的 runtime version 构建的。要修复此问题,请配置我们的 runtime version并重新构建应用。

意外的 branch

如果 deployment 关联到了不符合预期的 branch,我们需要将我们的 channel 映射到正确的 branch

缺少更新

所显示的 deployment 没有任何更新。要修复此问题,请向该 branch 发布更新。如果更新已经发布,请检查 Updates 页面,确保它与我们构建的 runtime version 匹配。

缺少 branch

所显示的 deployment 具有正确的 channel,但未链接到任何 branch。要修复此问题,请将 channel 映射到正确的 branch

缺少 deployment

如果我们的 deployment 没有显示出来,这意味着我们的构建没有为 EAS Update 正确配置。要修复此问题,请配置我们的 channel配置我们的 runtime version,并验证我们的通用配置。在进行这些更改后,我们需要重新构建应用。

更新崩溃时自动回滚

如果 Deployments 页面上一切看起来都正确,但你的应用仍然显示之前的更新或构建中嵌入的代码,那么新更新中的代码可能发生了崩溃。这种情况可能发生在应用下载并应用新更新后,新更新在根组件渲染之前崩溃。

EAS Update 的设计是在检测到新更新在启动后不久崩溃时,自动回滚到上一个更新。有关更多信息,请参见EAS Update 如何检测崩溃并回滚到之前的可用版本

要诊断导致更新崩溃的错误:

  • 查看 运行时问题排查指南,以应用一种识别错误的策略。
  • 在识别出错误后,发布一个修复了崩溃的新更新来解决该问题。

新更新不工作但嵌入代码可以工作的一个常见原因是缺少环境变量。有关更多信息,请参见环境变量如何与 EAS Update 配合使用

无法加载所有资源

如果你的用户看到“Failed to load all assets”错误,这意味着应用能够下载 manifest,但无法下载更新运行所需的所有资源。如果某个资源不在原始构建中,就需要下载它。常见原因包括:

  • 向更新中添加了许多大型资源,而应用由于网络问题无法全部下载。
  • 用户的互联网连接较差。
  • 用户所在国家/地区会屏蔽或限制 Cloudflare IP 地址,而 EAS Update 使用这些地址来提供资源。

要诊断资源加载问题:

  • 通过检查 资源列表,验证用户下载了哪些资源以及它们的大小。
  • 在你自己的设备上重现该问题,并检查从原生层暴露出来的 expo-updates 日志条目
  • 如果你已在 Sentry 等服务中记录了此错误,请检查遇到错误的用户的 IP 地址,并确认其不在已知会屏蔽或限制 Cloudflare IP 地址的国家/地区。

解决方案

配置 channel

要验证某个构建具有特定的 channel,请确保 eas.json 中的构建配置文件包含 channel 属性:

eas.json
{ "build": { "preview": { "distribution": "internal", "channel": "preview" }, "production": { "channel": "production" } } }

然后,我们可以运行类似 eas build --profile preview 的命令来创建一个名为 "preview" 的 channel 的构建。

配置 runtime version

要验证我们的 runtime version,请确保我们的应用配置(app.json/app.config.js)包含 runtimeVersion 属性:

app.json
{ "expo": { "runtimeVersion": { "policy": "appVersion" } } }

默认情况下,它是 { "policy": "appVersion" },但我们可以将运行时改为使用不同的策略或特定版本。然后,我们可以运行类似 eas build --profile preview 的命令来创建一个具有我们期望的 runtime version 的构建。

将 channel 映射到 branch

如果 channel 没有映射到我们期望的 branch,我们可以使用以下命令更改关联:

Terminal
# eas channel:edit [channel-name] --branch [branch-name]
# 示例
eas channel:edit production --branch release-1.0

如果我们的 branch 没有列出,我们可以使用 eas branch:create 创建一个新的 branch。

发布更新

要创建并发布更新,我们可以运行以下命令:

Terminal
eas update

发布后,输出将显示 branch 和 runtime version。这些信息可以帮助我们验证我们创建的更新是否符合预期配置。

通用策略

在使用本指南中提到的更具体方法之前,请先尝试这些策略。

使用 expo-dev-client

创建一个开发版构建。它将帮助我们在有问题的构建中预览已发布的更新。

应用内调试

expo-updates 库导出了一系列函数,用于在应用已运行后与更新交互。在某些情况下,调用获取更新并查看错误信息,可以帮助我们缩小问题根因范围。我们可以为项目制作一个模拟器构建,并手动检查是否有可用更新,或者在获取更新时是否出现错误。

配置问题

尽管遵循了基础指南,我们的应用仍然没有收到预期的更新。

expo-updates 配置

expo-updates 库运行在最终用户的应用中,并向更新服务器发送请求以获取最新更新。

验证应用配置

当我们设置 EAS Update 时,很可能运行了 eas update:configure 来配置 expo-updates 以配合 EAS Update 工作。此命令会修改我们的应用配置(app.json/app.config.js)。以下是我们期望看到的字段:

  • runtimeVersion 应该已设置。默认情况下,它是 { "policy": "appVersion" }。如果我们的项目有 androidios 目录,我们就必须手动设置 runtimeVersion
  • updates.url 应该是类似 https://u.expo.dev/your-project-id 的值,其中 your-project-id 与我们项目的 ID 对应。我们可以在我们的网站上看到这个 ID。
  • updates.enabled 不应为 false。如果未指定,默认值为 true

最后,确保 package.json 中包含 expo-updates。如果没有,请运行:

Terminal
npx expo install expo-updates

在 prebuild 之后检查 expo-updates 配置

每当我们运行 eas build 时,EAS 服务器都会在我们的项目上运行 npx expo prebuild 命令,以解包包含原生文件的 androidios 目录。这样 EAS Build 就可以构建任何项目,无论它是否包含原生文件。

如果我们的项目没有 androidios 目录,我们可以先提交任何现有更改,然后运行 npx expo prebuild 来检查 EAS Build 将要作用的项目状态。运行后,请查找以下文件:android/app/src/main/AndroidManifest.xmlios/your-project-name/Supporting/Expo.plist

在这两个文件中,我们都期望看到 EAS Update URL 和 runtime version 的配置。以下是我们期望在每个文件中看到的属性:

AndroidManifest.xml
%%placeholder-start%%... %%placeholder-end%% <meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="your-runtime-version-here"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/your-project-id-here"/> %%placeholder-start%%... %%placeholder-end%%
Expo.plist
%%placeholder-start%%... %%placeholder-end%% <key>EXUpdatesRuntimeVersion</key> <string>your-runtime-version-here</string> <key>EXUpdatesURL</key> <string>https://u.expo.dev/your-project-id-here</string> %%placeholder-start%%... %%placeholder-end%%

不使用 EAS Build 时的配置

如果我们没有使用 EAS Build,本节将指导我们调试项目中 EAS Update 的状态。我们需要查看系统中的多个位置。下面是一张 EAS Update 工作方式的示意图,以及在查找问题根因时有用的检查点。在接下来的章节中,我们将检查并验证这些位置以及更多内容。

验证构建配置

请遵循本地构建指南来配置应用的 channel 和 runtime version。我们还需要确保我们的通用配置正确无误。

验证 channel

构建具有一个名为 channel 的属性,EAS Update 使用它来链接到 branch。一个 channel 通常会分配给多个特定平台的构建。例如,我们可能有一个 Android 构建和一个 iOS 构建,它们的 channel 都名为 "production"

一旦构建有了 channel 名称,我们就可以通过检查 Channels 页面来确保 EAS 服务器知道它。

我们期望该页面显示与我们的构建相同的 channel 名称。如果那里没有,我们可以在 EAS 服务器上创建该 channel,使用:

Terminal
# eas channel:create [channel-name]
# 示例
eas channel:create production

验证 channel/branch 映射

开发者会在 channel 和 branch 之间定义一个链接。当 channel 和 branch 关联后,带有该 channel 的应用会从关联的 branch 获取最新的兼容更新。

Channels 页面 会在映射存在时显示 channel 到 branch 的映射关系。

如果 channel 没有链接到我们期望的 branch,可以使用以下命令更改链接:

Terminal
# eas channel:edit [channel-name] --branch [branch-name]
# 示例
eas channel:edit production --branch release-1.0

验证 update

每个 branch 都包含一个 update 列表。当构建请求一个 update 时,我们会先找到该构建的 channel,然后找到该 channel 关联的 branch。一旦找到 branch,EAS 就会返回该 branch 上最新的兼容 update。当构建和 update 具有相同的 runtime version 和 platform 时,它们就是兼容的。

要查看某个 branch 上有哪些 update,可以前往 Branches 页面 并选择我们感兴趣的 branch。

Branch Detail 页面会显示一个 update 列表及其 runtime version 和 platform。根据这个列表,我们应该能够通过将构建的 runtime version 和 platform 与 update 的 runtime version 和 platform 进行匹配,来判断某个构建应应用哪个 update。最新的兼容 update 将可供构建下载并执行。

调试 EAS Update

在验证 expo-updates 和 EAS Update 配置之后,我们可以继续调试项目与 updates 的交互方式。

应用内调试

expo-updates 库导出了一系列函数,用于在应用已经运行时与 updates 交互。在某些情况下,发起一次获取 update 的请求并查看错误信息,可以帮助我们缩小根本原因的范围。我们可以为项目制作一个模拟器构建,并手动检查是否有可用 updates,或者在获取 updates 时是否出现错误。有关代码示例,请参阅手动检查更新

手动检查构建

将项目构建为应用时,可能会有多个步骤会改变 npx expo prebuild 的输出。完成构建后,可以打开构建产物并检查原生文件,以查看其最终配置。

以下是在 macOS 上检查 iOS Simulator 构建的步骤:

  1. 使用 EAS Build 创建应用的 iOS Simulator 构建。这可以通过在构建配置中添加 "ios": { "simulator": true } 来完成。
  2. 构建完成后,下载结果并解压。
  3. 然后右键单击应用并选择“显示包内容”。
  4. 接着,我们可以检查 Expo.plist 文件。

Expo.plist 文件中,我们期望看到以下配置:

Expo.plist
%%placeholder-start%%... %%placeholder-end%% <key>EXUpdatesRequestHeaders</key> <dict> <key>expo-channel-name</key> <string>your-channel-name</string> </dict> <key>EXUpdatesRuntimeVersion</key> <string>your-runtime-version</string> <key>EXUpdatesURL</key> <string>https://u.expo.dev/your-project-id</string> %%placeholder-start%%... %%placeholder-end%%

手动检查 manifest

当使用 EAS Update 发布 update 时,我们会创建一个 manifest,供终端用户应用请求。manifest 中包含诸如加载 update 所需的资产和版本等信息。我们可以通过浏览器访问特定 URL,或者使用 curl 来检查 manifest。

在我们项目的 app config(app.json/app.config.json)中,可以通过 updates.url 找到可 GET 的 URL。

这个 url 是 EAS 的 https://u.expo.dev 域名,后面跟着 EAS 服务器上该项目的 ID。如果直接访问这个 URL,会看到一个缺少 header 的错误。我们可以通过向 URL 添加三个查询参数来查看 manifest:runtime-versionchannel-nameplatform。如果我们发布了一个 runtime version 为 1.0.0、channel 为 production、platform 为 android 的 update,那么可访问的完整 URL 会类似于这样:

https://u.expo.dev/your-project-id?runtime-version=1.0.0&channel-name=production&platform=android

查看网络请求

识别问题根本原因的另一种方式是查看应用向 EAS 服务器发出的网络请求,然后查看响应。我们建议使用 ProxymanCharles Proxy 之类的工具来监视应用的网络请求。

无论使用哪种工具,我们都需要按照其说明安装 SSL 证书,以便该工具能够解码 HTTPS 请求。一旦在模拟器或真机上完成设置,我们就可以打开应用并查看请求。

我们关注的请求来自 https://u.expo.devhttps://assets.eascdn.net。来自 https://u.expo.dev 的响应会包含 update manifest,其中指定了应用运行该 update 需要获取哪些资产。来自 https://assets.eascdn.net 的响应会包含资产,例如图片、字体文件等,这些都是运行该 update 所必需的。

在检查对 https://u.expo.dev 的请求时,我们可以查看以下请求头:

  • Expo-Runtime-Version:这应该与我们制作构建和 update 时使用的 runtime version 一致。
  • expo-channel-name:这应该是 eas.json 构建配置中指定的 channel 名称。
  • Expo-Platform:这应该是 "android""ios"

对于所有请求,我们期望看到 200 响应码;如果没有任何变化,则期望看到 304

下面是一张显示成功获取 update manifest 的请求截图:

运行时问题

我们能够加载到预期的 update,但项目显示了意外的行为。

通过 expo-updates 加载应用时调试原生代码

默认情况下,我们需要制作 release 构建,才能启用 expo-updates 并加载 updates,而不是从开发服务器读取。这是因为 debug 构建的行为与普通 React Native 项目的 debug 构建相同。

为了更方便地在更接近生产环境的场景中测试和调试原生代码,请按照以下步骤创建启用了 expo-updates 的应用 debug 构建。

我们还提供了一个逐步指南,帮助快速试用 EAS Update,可在本地开发环境中使用 Android Studio 或 Xcode,通过 release 或 debug 构建来完成。

Android 本地构建

iOS 本地构建

  • 设置调试环境变量:export EX_UPDATES_NATIVE_DEBUG=1
  • 重新安装 pods:npx pod-install。现在 expo-updates 的 podspec 会检测这个环境变量,并进行相应修改,从而绕过通常会从 Metro packager 加载的调试代码,并使用 EXUpdates bundle 及其他从 EAS 加载 updates 所需的依赖来构建应用。
  • 确保在我们的 Expo.plist 中设置了所需的 channel
  • 修改应用的 Xcode 项目文件,强制在 release 和 debug 构建中都打包应用的 JavaScript:
    Terminal
    sed -i '' 's/SKIP_BUNDLING/FORCE_BUNDLING/g;' ios/<project name>.xcodeproj/project.pbxproj
  • 使用 Xcode 或命令行执行应用的debug 构建

EAS Build

或者,我们也可以使用 EAS 创建一个启用了 expo-updates 的 debug 构建。环境变量在 eas.json 中设置,如下例所示:

eas.json
{ "build": { "preview_debug": { "env": { "EX_UPDATES_NATIVE_DEBUG": "1" }, "android": { "distribution": "internal", "withoutCredentials": true, "gradleCommand": ":app:assembleDebug" }, "ios": { "simulator": true, "buildConfiguration": "Debug" }, "channel": "preview_debug" } } }

发布问题

我们无法发布 update,或者 update 的某些部分没有按预期发布。

在本地检查最新 update

当我们使用 EAS Update 发布 update 时,它会在本地项目根目录下创建一个 /dist 目录,其中包含作为该 update 一部分上传的资产。

查看 update 中包含的所有资产

查看 update bundle 中包含哪些资产可能会有帮助。我们可以在 Updates Detail 页面中查看命名资产列表:

或者在本地运行:

Terminal
npx expo export

缓解步骤

一旦我们找到问题的根本原因,就可以考虑采取各种缓解步骤。最常见的问题之一是发布了一个包含 bug 的 update。发生这种情况时,我们可以重新发布之前的 update 来解决问题。

重新发布之前的 update

“撤销”一次有问题发布的最快方法,是重新发布一个已知正常的 update。假设我们有一个包含两个 update 的 branch:

Terminal
branch: "production"updates: [ update 2 (id: xyz2) "修复拼写错误" // 有问题的 update update 1 (id: abc1) "更新颜色" // 正常的 update]

如果“update 2”被证明是有问题的 update,我们可以使用如下命令重新发布“update 1”:

Terminal
# eas update:republish --group [update-group-id]# eas update:republish --branch [branch-name]
# 示例
eas update:republish --group abc1
eas update:republish --branch production

上面的示例命令会使 branch 变成如下状态:

Terminal
branch: "production"updates: [ update 3 (id: def3) "更新颜色" // 重新发布 update 1 (id: abc1) update 2 (id: xyz2) "修复拼写错误" // 有问题的 update update 1 (id: abc1) "更新颜色" // 正常的 update]

由于“update 3”现在是“production” branch 上最新的 update,今后所有查询 update 的用户都会收到“update 3”,而不是有问题的 update“update 2”。

虽然这可以防止所有新用户看到有问题的 update,但已经收到该有问题 update 的用户会继续运行它,直到他们能够下载最新的 update。由于移动网络并不总是能够下载最新的 update,因此有时用户可能会长时间运行一个有问题的 update。在查看应用的错误日志时,如果用户的应用正在获取最新的 update 或构建,出现持续存在的长尾错误是正常的。当我们看到错误率显著下降时,就知道我们已经解决了这个 bug;不过,如果我们的用户分布在许多地区和移动网络上,那么错误率很可能不会完全消失。