了解有关如何调试 EAS 更新的高级策略。
在 基本指南 中验证 EAS 更新配置后,我们可以继续采用更高级的调试策略。以下部分描述了常见的问题类别以及解决这些问题的策略。
¥After verifying our EAS Update configuration in our basic guide, we can move on to more advanced debugging strategies. The following sections describe common classes of problems and strategies on how to tackle them.
¥General strategies
在使用本指南中提到的更具体的策略之前,请先尝试这些策略。
¥Try these strategies before using the more specific ones mentioned in this guide.
expo-dev-client
¥Use expo-dev-client
创建 我们构建的开发版本。它将帮助我们预览有问题的版本中已发布的更新。
¥Create a development version of our build. It will help us preview published updates inside a problematic build.
¥In-app debugging
一旦应用已经运行,expo-updates
库就会导出各种函数来与更新交互。在某些情况下,调用获取更新并查看错误消息可以帮助我们缩小根本原因的范围。我们可以对项目进行模拟器构建,并手动检查更新是否可用或者获取更新时是否出现错误。
¥The expo-updates
library exports a variety of functions to interact with updates once the app is already running. In certain cases, making a call to fetch an update and seeing an error message can help us narrow down the root cause. We can make a simulator build of the project and manually check to see if updates are available or if there are errors when fetching updates.
打印 Update.Constants 以验证我们的配置。
¥Print the Update.Constants to verify our configuration.
检查日志条目 从原生层浮出水面。
¥Examine log entries surfaced from the native layer.
获取并 手动加载更新。
¥Fetch and load updates manually.
¥Configuration issues
尽管遵循了 基本指南,我们的应用仍然没有收到预期的更新。
¥Our app is still not receiving the expected update despite following the basic guide.
expo-updates
配置¥expo-updates
configuration
expo-updates
库在终端用户的应用内运行,并向更新服务器发出请求以获取最新更新。
¥The expo-updates
library runs inside an end-user's app and makes requests to an update server to get the latest update.
¥Verifying app configuration
当我们设置 EAS 更新时,我们可能运行 eas update:configure
来配置 expo-updates 以与 EAS 更新配合使用。此命令更改我们的应用配置 (app.json/app.config.js)。以下是我们期望看到的字段:
¥When we set up EAS Update, we likely ran eas update:configure
to configure expo-updates to work with EAS Update. This command makes changes to our app config (app.json/app.config.js). Here are the fields we'd expect to see:
应设置 runtimeVersion
。默认为 { "policy": "sdkVersion" }
。如果我们的项目有 android 和 ios 目录,我们必须手动设置 runtimeVersion
。
¥runtimeVersion
should be set. By default, it is { "policy": "sdkVersion" }
. If our project has android and ios directories, we'll have to set the runtimeVersion
manually.
updates.url
应该是类似于 https://u.expo.dev/your-project-id
的值,其中 your-project-id
与我们项目的 ID 匹配。我们可以在 我们的网站 上看到这个 ID。
¥updates.url
should be a value like https://u.expo.dev/your-project-id
, where your-project-id
matches the ID of our project. We can see this ID on our website.
updates.enabled
不应该是 false
。如果不指定则默认为 true
。
¥updates.enabled
should not be false
. It's true
by default if it is not specified.
最后,确保 expo-updates
包含在 package.json 中。如果不是,请运行:
¥Finally, make sure that expo-updates
is included in package.json. If it's not, run:
-
npx expo install expo-updates
¥Inspecting expo-updates configuration after prebuild
每当我们运行 eas build
时,都会在 EAS 服务器上的项目上运行 npx expo prebuild
命令,以解压包含原生文件的 android 和 ios 目录。这使得 EAS Build 可以构建任何项目,无论它是否包含原生文件。
¥Whenever we run eas build
, the npx expo prebuild
command is run on our project on EAS servers to unpack the android and ios directories that contain native files. This makes it so EAS Build can build any project, whether it includes the native files or not.
如果我们的项目没有 android 或 ios 目录,我们可以提交任何现有更改,然后运行 npx expo prebuild
来检查 EAS Build 将执行的项目状态。运行后,查找以下文件:android/app/src/main/AndroidManifest.xml 和 ios/your-project-name/Supporting/Expo.plist。
¥If our project does not have android or ios directories, we can make commit any existing changes, then run npx expo prebuild
to inspect the project state that EAS Build will act on. After running this, look for the following files: android/app/src/main/AndroidManifest.xml and ios/your-project-name/Supporting/Expo.plist.
在每个中,我们期望看到 EAS 更新 URL 和运行时版本的配置。以下是我们希望在每个文件中看到的属性:
¥In each, we expect to see configuration for the EAS Update URL and the runtime version. Here are the properties we'd expect to see in each file:
%%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%%
%%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%%
¥Configuration without EAS Build
如果我们不使用 EAS Build,本节将逐步调试项目中 EAS Update 的状态。我们需要查看系统中的多个点。下图显示了 EAS 更新的工作原理以及在查找问题根本原因时可用于检查的点。在接下来的部分中,我们将检查并验证这些点以及更多内容。
¥If we aren't using EAS Build, this section will walk through debugging the state of EAS Update in our project. We'll need to look at multiple spots in the system. Below is a diagram of how EAS Update works and the spots that are useful to inspect when finding the root cause of an issue. In the sections following, we'll inspect and verify these spots and more.
¥Verify build configuration
按照 本地构建指南 配置我们应用的通道和运行时版本。我们还需要确保 通用配置 正确。
¥Follow the Building Locally guide to configure our app's channel and runtime version. We'll also need to make sure our general configuration is correct.
¥Verify the channel
构建有一个名为 channel
的属性,EAS 更新使用该属性链接到分支。通常会为多个特定于平台的构建提供一个通道。例如,我们可能有一个 Android 版本和一个 iOS 版本,两者都具有名为 "production"
的通道。
¥Builds have a property named channel
, which EAS Update uses to link to a branch. A channel is often given to multiple platform-specific builds. For instance, we might have an Android build and an iOS build, both with a channel named "production"
.
一旦构建有了通道名称,我们就可以通过检查 通道页面.conf 文件来确保 EAS 服务器知道它。
¥Once a build has a channel name, we can make sure that EAS servers know about it by checking the Channels page.
我们希望页面显示与我们的构建相同的通道名称。如果不存在,我们可以使用以下命令在 EAS 服务器上创建通道:
¥We'd expect the page to display the same channel name that our build has. If it's not there, we can create the channel on EAS servers with:
# eas channel:create [channel-name]
# Example
-
eas channel:create production
¥Verify the channel/branch mapping
开发者在通道和分支之间定义了一个链接。当通道和分支链接时,具有通道的应用将获得链接分支上的最新兼容更新。
¥There is a link that is defined by the developer between a channel and a branch. When a channel and branch are linked, an app with a channel will get the most recent compatible update on the linked branch.
通道页面 将显示通道到分支映射(如果存在)。
¥The Channels page will display the channel to branch mapping if it exists.
如果通道未链接到我们期望的分支,我们可以使用以下命令更改链接:
¥If the channel is not linked to the branch we expect, we can change the link with:
# eas channel:edit [channel-name] --branch [branch-name]
# Example
-
eas channel:edit production --branch release-1.0
¥Verify the update
每个分支都包含一个更新列表。当构建调用更新时,我们会找到构建的通道,然后找到链接到该通道的分支。一旦找到分支,EAS 将返回该分支上的最新兼容更新。当构建和更新共享相同的运行时版本和平台时,它们是兼容的。
¥Every branch contains a list of updates. When a build makes a call for an update, we find the channel of the build, then the branch linked to that channel. Once the branch is found, EAS will return the most recent compatible update on that branch. A build and an update are compatible when they share the same runtime version and platform.
要检查分支上有哪些更新,我们可以转到 分行页面 并选择我们感兴趣的分支。
¥To inspect which updates are on a branch, we can go to the Branches page and choose our branch of interest.
分支详细信息页面将向我们显示更新列表及其运行时版本和平台。从这个列表中,我们应该能够通过将构建的运行时版本和平台与更新的运行时版本和平台相匹配来找出应该应用于给定构建的更新。兼容的最新更新将可供构建下载和执行。
¥The Branch Detail page will show us a list of updates and their runtime versions and platforms. From this list, we should be able to figure out which update should apply to a given build, by matching the build's runtime version and platform to update's runtime version and platform. The most recent update that is compatible will be available for a build to download and execute.
¥Debugging EAS Update
验证 expo-updates
和 EAS 更新配置后,我们可以继续调试我们的项目如何与更新交互。
¥After verifying expo-updates
and EAS Update configurations, we can move on to debugging how our project is interacting with updates.
¥In-app debugging
一旦应用已经运行,expo-updates
库就会导出各种函数来与更新交互。在某些情况下,调用获取更新并查看错误消息可以帮助我们缩小根本原因的范围。我们可以对项目进行模拟器构建,并手动检查更新是否可用或者获取更新时是否出现错误。请参阅 手动检查更新 的代码示例。
¥The expo-updates
library exports a variety of functions to interact with updates once the app is already running. In certain cases, making a call to fetch an update and seeing an error message can help us narrow down the root cause. We can make a simulator build of the project and manually check to see if updates are available or if there are errors when fetching updates. See the code example to check for updates manually.
¥Inspecting a build manually
将项目构建到应用中时,可能有多个步骤会改变 npx expo prebuild
的输出。进行构建后,可以打开构建的内容并检查原生文件以查看其最终配置。
¥When building a project into an app, there can be multiple steps that alter the output of npx expo prebuild
. After making a build, it is possible to open the build's contents and inspect native files to see its final configuration.
以下是检查 macOS 上的 iOS 模拟器版本的步骤:
¥Here are the steps for inspecting an iOS Simulator build on macOS:
使用 EAS Build 创建应用的 iOS 模拟器版本。这是通过将 "ios": { "simulator": true }
添加到构建配置文件来完成的。
¥Create an iOS Simulator build of the app using EAS Build. This is done by adding "ios": { "simulator": true }
to a build profile.
构建完成后,下载结果并解压缩。
¥Once the build is finished, download the result and unzip it.
然后,右键单击该应用并选择 "显示封装内容"。
¥Then, right click on the app and select "Show Package Contents".
从那里,我们可以检查 Expo.plist 文件。
¥From there, we can inspect the Expo.plist file.
在 Expo.plist 文件中,我们期望看到以下配置:
¥Inside the Expo.plist file, we expect to see the following configurations:
%%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%%
¥Inspecting manifests manually
当使用 EAS 更新发布更新时,我们会根据终端用户应用的请求创建一个清单。清单包含加载更新所需的资源和版本等信息。我们可以通过访问浏览器中的特定 URL 或使用 curl
.txt 来检查清单。
¥When an update is published with EAS Update, we create a manifest that end-user app's request. The manifest has information like which assets and versions are needed for an update to load. We can inspect the manifest by going to a specific URL in a browser or by using curl
.
在我们项目的应用配置(app.json/app.config.json)中,我们可以获取的 URL 位于 updates.url
下。
¥Inside our project's app config (app.json/app.config.json), the URL we can GET is under updates.url
.
这个 url
是 EAS 的“https://u.expo.dev”域,后面是 EAS 服务器上的项目 ID。如果我们直接访问 URL,我们会看到有关缺少标头的错误。我们可以通过向 URL 添加三个查询参数来查看清单:runtime-version
、channel-name
和 platform
。如果我们发布运行时版本为 1.0.0
、通道为 production
、平台为 android
的更新,我们可以访问的完整 URL 将类似于以下内容:
¥This url
is EAS' "https://u.expo.dev" domain, followed by the project's ID on EAS servers. If we go to the URL directly, we'll see an error about missing a header. We can view a manifest by adding three query parameters to the URL: runtime-version
, channel-name
, and platform
. If we published an update with a runtime version of 1.0.0
, a channel of production
and a platform of android
, the full URL we could visit would be similar to this:
https://u.expo.dev/your-project-id?runtime-version=1.0.0&channel-name=production&platform=android
¥Viewing network requests
确定问题根本原因的另一种方法是查看应用向 EAS 服务器发出的网络请求,然后查看响应。我们建议使用 代理人 或 Charles 代理 等程序来监视来自我们应用的网络请求。
¥Another way to identify the root cause of an issue is to look at the network requests that the app is making to EAS servers, then viewing the responses. We recommend using a program like Proxyman or Charles Proxy to watch network requests from our app.
对于任一程序,我们都需要按照其说明安装 SSL 证书,以便该程序可以解码 HTTPS 请求。一旦在模拟器或实际设备上设置完毕,我们就可以打开我们的应用并监视请求。
¥With either program, we'll need to follow their instructions for installing an SSL certificate, so that the program can decode HTTPS requests. Once that's set up in a simulator or on an actual device, we can open our app and watch requests.
我们感兴趣的请求来自 https://u.expo.dev 和 https://assets.eascdn.net。来自 https://u.expo.dev 的响应将包含更新清单,其中指定应用需要获取哪些资源来运行更新。来自 https://assets.eascdn.net 的响应将包含运行更新所需的资源,例如图片、字体文件等。
¥The requests we're interested in are from https://u.expo.dev and https://assets.eascdn.net. Responses from https://u.expo.dev will contain an update manifest, which specifies which assets the app will need to fetch to run the update. Responses from https://assets.eascdn.net will contain assets, like images, font files, and so on, that are required for the update to run.
在检查对 https://u.expo.dev 的请求时,我们可以查找以下请求标头:
¥When inspecting the request to https://u.expo.dev, we can look for the following request headers:
Expo-Runtime-Version
:这应该成为我们构建和更新的运行时版本。
¥Expo-Runtime-Version
: this should make the runtime version we made our build and update with.
expo-channel-name
:这应该是 eas.json 构建配置文件中指定的通道名称。
¥expo-channel-name
: this should be the channel name specified in the eas.json build profile.
Expo-Platform
:这应该是 "android" 或 "ios"。
¥Expo-Platform
: this should be either "android" or "ios".
对于所有请求,我们期望看到 200
响应代码,或者 304
(如果没有任何更改)。
¥As for all requests, we expect to see either 200
response codes, or 304
if nothing has changed.
下面是显示成功更新清单请求的屏幕截图:
¥Below is a screenshot showing the request of a successful update manifest request:
¥Runtime issues
我们能够加载预期的更新,但我们的项目显示出意外的行为。
¥We are able to load the expected update but our project is displaying unexpected behavior.
¥Debugging of native code while loading the app through expo-updates
默认情况下,我们需要为 expo-updates
进行发布构建以启用并加载更新,而不是从开发服务器读取。这是因为调试构建的行为类似于普通的 React Native 项目调试构建。
¥By default, we need to make a release build for expo-updates
to be enabled and to load updates rather than reading from a development server. This is because debug builds behave like normal React Native project debug builds.
为了更轻松地在更接近生产的环境中测试和调试原生代码,请按照以下步骤创建启用了 expo-updates
的应用的调试版本。
¥To make it easier to test and debug native code in an environment that is closer to production, follow the steps below to create a debug build of the app with expo-updates
enabled.
我们还在使用 Android Studio 或 Xcode 的本地开发环境中提供 快速尝试 EAS 更新的分步指南,以及应用的发布或调试版本。
¥We also provide a step-by-step guide to try out EAS Update quickly in a local development environment using Android Studio or Xcode, with either release or debug builds of the app.
¥iOS local builds
设置调试环境变量:export EX_UPDATES_NATIVE_DEBUG=1
¥Set the debug environment variable: export EX_UPDATES_NATIVE_DEBUG=1
使用 npx pod-install
重新安装 Pod。expo-updates
podspec 现在检测此环境变量,并进行更改,以便绕过通常从 Metro 打包程序加载的调试代码,并且应用是使用 EXUpdates 打包包和从 EAS 加载更新所需的其他依赖构建的。
¥Reinstall pods with npx pod-install
. The expo-updates
podspec now detects this environment variable, and makes changes so that the debug code that would normally load from the Metro packager is bypassed, and the app is built with the EXUpdates bundle and other dependencies needed to load updates from EAS.
修改应用 Xcode 项目文件以强制打包应用 JavaScript 以用于发布和调试版本:
¥Modify the application Xcode project file to force bundling of the application JavaScript for both release and debug builds:
-
sed -i '' 's/SKIP_BUNDLING/FORCE_BUNDLING/g;' ios/<project name>.xcodeproj/project.pbxproj
使用 Xcode 或从命令行执行应用的 调试构建。
¥Execute a debug build of the app with Xcode or from the command line.
¥Android local builds
设置调试环境变量:export EX_UPDATES_NATIVE_DEBUG=1
¥Set the debug environment variable: export EX_UPDATES_NATIVE_DEBUG=1
确保在 AndroidManifest.xml 中设置所需的通道
¥Ensure the desired channel is set in your AndroidManifest.xml
使用 Android Studio 或从命令行执行应用的 调试构建。
¥Execute a debug build of the app with Android Studio or from the command line.
¥EAS Build
或者,我们可以使用 EAS 创建启用 expo-updates
的调试版本。环境变量在 eas.json 中设置,如下例所示:
¥Alternatively, we can use EAS to create a debug build where expo-updates
is enabled. The environment variable is set in eas.json, as shown in the example below:
{
"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"
}
}
}
¥Publishing issues
我们无法发布更新,或者部分更新未按预期发布。
¥We are not able to publish an update, or parts of our update are not being published as expected.
¥Inspecting the latest update locally
当我们使用 EAS Update 发布更新时,它会在本地项目的根目录中创建一个 /dist 文件夹,其中包含作为更新的一部分上传的资源。
¥When we publish an update with EAS Update, it creates a /dist folder in the root of our project locally, which includes the assets that were uploaded as a part of the update.
¥Viewing all assets included in an update
查看我们的更新包中包含哪些资源可能会有所帮助。我们可以通过运行以下命令查看指定资源的列表:
¥It may be helpful to see which assets are included in our update bundle. We can see a list of named assets by running:
-
npx expo export
¥Mitigation steps
一旦我们找到了问题的根本原因,我们可能需要采取各种缓解措施。最常见的问题之一是推送内部存在错误的更新。发生这种情况时,我们可以重新发布以前的更新来解决问题。
¥Once we've found the root cause of the issue, there are various mitigation steps we might want to take. One of the most common problems is pushing an update that has a bug inside it. When this happens, we can re-publish a previous update to resolve the issue.
¥Re-publishing a previous update
修复错误发布的最快方法是重新发布已知良好的更新。想象一下我们有一个有两个更新的分支:
¥The fastest way to "undo" a bad publish is to re-publish a known good update. Imagine we have a branch with two updates:
branch: "production"
updates: [
update 2 (id: xyz2) "fixes typo" // bad update
update 1 (id: abc1) "updates color" // good update
]
如果 "更新 2" 被证明是一个错误的更新,我们可以使用如下命令重新发布 "更新 1":
¥If "update 2" turned out to be a bad update, we can re-publish "update 1" with a command like this:
# eas update:republish --group [update-group-id]
# eas update:republish --branch [branch-name]
# Example
-
eas update:republish --group abc1
-
eas update:republish --branch production
上面的示例命令将产生一个现在如下所示的分支:
¥The example command above would result in a branch that now appears like this:
branch: "production"
updates: [
update 3 (id: def3) "updates color" // re-publish of update 1 (id: abc1)
update 2 (id: xyz2) "fixes typo" // bad update
update 1 (id: abc1) "updates color" // good update
]
由于 "更新 3" 现在是 "production" 分支上的最新更新,因此将来查询更新的所有用户都将收到 "更新 3",而不是错误的更新 "更新 2"。
¥Since "update 3" is now the most recent update on the "production" branch, all users who query for an update in the future will receive "update 3" instead of the bad update, "update 2".
虽然这将阻止所有新用户看到错误更新,但已经收到错误更新的用户将运行它,直到他们可以下载最新更新。由于移动网络并不总是能够下载最新的更新,有时用户可能会长时间运行错误的更新。查看应用的错误日志时,当用户的应用获得最新更新或构建时,看到挥之不去的长尾错误是正常的。当我们看到错误率急剧下降时,我们就知道我们解决了这个错误;然而,如果我们在许多地点和移动网络上拥有多样化的用户群,它可能不会完全消失。
¥While this will prevent all new users from seeing the bad update, users who've already received the bad update will run it until they can download the latest update. Since mobile networks are not always able to download the most recent update, sometimes users may run a bad update for a long time. When viewing error logs for our app, it's normal to see a lingering long tail of errors as our users' apps get the most recent update or build. We'll know we solved the bug when we see the error rate decline dramatically; however, it likely will not disappear completely if we have a diverse user base across many locations and mobile networks.