This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
在运行时覆盖更新配置
了解如何在运行时覆盖更新 URL 和请求标头,以控制在客户端加载哪些更新。
使用 EAS 更新的典型方式是,在应用构建中嵌入一个单一的更新 URL 和一组请求头(例如更新通道名称)。要控制加载哪个更新,可以通过 eas update 命令或 EAS 仪表板在服务器上进行更改。例如,你将一个新更新发布到你的构建所指向的通道,那么构建将在下一次启动时获取该更新。发布到与你的构建指向的通道不同的通道的更新,将不会通过这种方式下载。
🌐 The typical way to use EAS Update is to have a single update URL and a set of request headers (such as update channel name) embedded in a build of your app. To control which update is loaded you make changes on the server through the eas update command or the EAS dashboard. For example, you publish a new update to a channel that your build is pointing to, then the build fetches that update on the next launch. Updates published to a channel different from the one your build is pointing to will not be downloaded with this approach.
本指南解释了如何在运行时更改更新 URL 和请求标头,从而可以通过 ID 加载特定更新或更改从中提取更新的渠道,而无需创建和安装新版本。
🌐 This guide explains how you can change the update URL and request headers at runtime, making it possible to load a specific update by ID or change the channel that updates are pulled from without creating and installing a new build.
覆盖请求标头
🌐 Override request headers
警告 本节中描述的功能在 Expo SDK 54 中可用,前提是使用
expo-updates版本 0.29.0 及更高版本。
对于使用 EAS 更新的应用,这个功能的主要用例是跳通道。跳通道在运行时会切换 expo-channel-name 请求头,作为完整通道切换工作流程的一部分。
🌐 For apps using EAS Update, the primary use case for this feature is channel surfing. Channel surfing switches the expo-channel-name request header at runtime as part of a complete channel switching workflow.
例如,如果你有一个用于生产更新的 default 通道和一个用于预览更新的 preview 通道,你可以覆盖 expo-channel-name 请求头来指向 preview 通道。这使你能够在当前的生产构建中测试预览更新。
🌐 For example, if you have a default channel for production updates and a preview channel for preview updates, you can override the expo-channel-name request header to point to the preview channel. This enables you to test preview updates in your current production builds.
警告 切换通道可能会导致兼容性问题,尤其是在更新使用不同迁移或数据结构的情况下。请参阅 切换通道时的风险与注意事项.
另一个潜在的用例是向不同的用户提供不同的更新,例如,让一组内部用户(例如员工)先于终端用户收到更新。
🌐 Another potential use case is to provide different updates to different users, for example, so that a group of internal users (such as employees) receive updates before end-users.
工作原理
🌐 How it works
你可以通过调用 Updates.setUpdateRequestHeadersOverride 来覆盖请求头。在 EAS Update 中,覆盖 expo-channel-name 请求头可以让应用从指定的通道请求更新。
🌐 You can override request headers by calling Updates.setUpdateRequestHeadersOverride. With EAS Update, overriding the expo-channel-name request header makes the app request updates from the specified channel.
要在运行时覆盖 expo-channel-name 头,原生构建必须包含它。当 eas.json 中的构建配置定义了 channel 时,EAS Build 会自动将该头设置为该值。使用 EAS Build 以外方式创建的构建(例如使用 npx expo run:android)不会自动设置它。对于这些构建,请在应用配置的 updates.requestHeaders 中声明该头,然后重新构建应用。
🌐 To override the expo-channel-name header at runtime, the native build must include it. When a build profile in eas.json defines channel, EAS Build sets this header automatically to that value. Builds created outside EAS Build (for example, with npx expo run:android) do not set it automatically. For those builds, declare the header in updates.requestHeaders in your app config, then rebuild the app.
提供一种方法让用户在你的应用中触发请求头的更改。这可以是一个只有可信用户才能访问的隐藏菜单,或者其他适合你使用场景的机制。更改请求头后,调用 fetchUpdateAsync() 来获取更新,并调用 reloadAsync() 来重新加载应用。你也可以等待下一次启动来自动获取和安装更新。
🌐 Provide a way for users to trigger the request header change in your app. This may be a hidden menu that only trusted users can access or another mechanism that fits your use case. After changing the request headers, call fetchUpdateAsync() to fetch the update and reloadAsync() to reload the app. You can also wait for the next launch to fetch and install the update automatically.
import * as Updates from 'expo-updates'; // Where you call this method depends on your use case - it may make sense to // have a menu in your preview builds that allows testers to pick from available channels, // for example: Updates.setUpdateRequestHeadersOverride({ 'expo-channel-name': 'preview' }); // You can fetch and reload the update immediately, or wait for the next launch await Updates.fetchUpdateAsync(); await Updates.reloadAsync();
我可以覆盖其他请求头吗?
是的。你想在运行时覆盖的任何其他头部都必须在你的应用配置中的 updates.requestHeaders 中声明。你传给 setUpdateRequestHeadersOverride() 的对象会替换构建中的所有自定义请求头,所以在覆盖生效期间,要把应用仍然需要的所有头部都包含进去。
🌐 Yes. Any other header you want to override at runtime must be declared in updates.requestHeaders in your app config. The object you pass to setUpdateRequestHeadersOverride() replaces all custom request headers from the build, so include every header the app still needs while the override is active.
覆盖更新 URL 和请求标头
🌐 Override both update URL and request headers
警告 本节描述的功能在 Expo SDK 52 中从
expo-updates版本 0.27.0 及更高版本可用。在预览环境中使用disableAntiBrickingMeasures选项。在生产应用中避免使用。
类似于覆盖请求头,如果你想进一步将更新 URL 覆盖为特定更新,你可以使用Updates.setUpdateURLAndRequestHeadersOverride 方法。这允许你通过 ID 加载特定更新,即使该更新在当前构建创建之前已发布。
🌐 Similar to override request headers, if you want to further override the update URL to a specific update, you can use the Updates.setUpdateURLAndRequestHeadersOverride method. This allows you to load a specific update by ID, even if the update is published before the current build is created.
在决定在生产环境中使用此功能之前,熟悉安全注意事项非常重要。将来,我们可能会增加对该功能的更受限制版本的支持,这将更适合此用例。
🌐 It is important to be familiar with the security considerations before deciding to use this feature in production. In the future, we may add support for a more restricted version of the feature that would be more suitable for this use case.
工作原理
🌐 How it works
有两个相关的 API:
🌐 There are two relevant APIs:
Updates.setUpdateURLAndRequestHeadersOverride({ updateUrl: string, requestHeaders: Object })- 这个方法会覆盖在 app.json/Expo.plist/AndroidManifest.xml 中指定的更新 URL 和请求头,比如expo-channel-name头。disableAntiBrickingMeasures- 应用配置中的这个字段会禁用expo-updates内置的防砖措施,这些措施可以确保后续更新总是可以发布,以修复之前安装的更新中的问题。当你更改这个值时,需要创建一个新的构建才能生效。不要在生产版本中启用它。 之所以这样命名,是为了清楚地表明,当你覆盖更新 URL/Headers 时,我们就无法安全地回滚到之前加载的更新。所以,如果你加载的新更新导致应用崩溃,expo-updates就无法自动恢复,因为这个字段与setUpdateURLAndRequestHeadersOverride一起会禁用嵌入式更新,因此不会有可回滚的更新。用户将需要卸载并重新安装应用。你应该只在预览版本中使用这个功能。
如何使用这些 API:
🌐 How to use these APIs:
- 覆盖更新 URL/请求头,提示用户关闭应用:在你的应用中的某个地方,你会提供一种方式让用户触发 URL 和/或请求头的更改。这可能是在只有受信任用户才能访问的隐藏菜单中,或者根据你的使用场景,通过其他机制实现。当参数被更改后,请通知用户需要关闭并重新打开应用,例如通过弹出提示。
expo-updates库(具有类似checkForUpdateAsync()的方法)在应用关闭并重新打开之前,不会使用新的覆盖 URL 和请求头。 - 新更新将在下次打开应用时下载并启动:在应用完全关闭(被“强制退出”,而不仅仅是置于后台)并重新打开后,更新及其相关资源将全部下载完成。一旦准备就绪,应用将启动。在下载过程中,用户需要停留在启动屏幕上。我们理解在启动屏等待并非理想体验,如果这一功能被广泛使用,我们计划在未来改善这一体验。对于当前推荐的使用场景(预览),这可能是一个可接受的权衡。
安全考虑
🌐 Security considerations
disableAntiBrickingMeasures 可以禁用防砖措施,这些措施保证了无论发布了什么更新,你都可以在之后发布另一条更新并成功应用。禁用防砖措施后,某些类型的攻击和漏洞可能会发生,特别是内部(受攻击的员工)发布恶意更新时。例如,有发布更新权限的员工可能会发布一个恶意更新,修改更新的 URL 和请求头指向自己的服务器,从而接管应用的安装。可以通过对生产更新使用 代码签名 并限制密钥的访问来减轻这种风险,但无法完全消除。
🌐 The anti-bricking measures that can be disabled with disableAntiBrickingMeasures ensure that, no matter what update is published, you can always publish another update afterwards that will be applied. By disabling the anti-bricking measures, certain categories of attacks and exploits become possible, especially around in-house (compromised employee) publishing of malicious updates. For example, an employee with the ability to publish updates could publish a malicious update that changes the update URL and request headers to point to their own server, and take over installations of the app. This risk can be mitigated, but not eliminated, by using code signing for production updates and limiting access to the key.
类似使用 CodePush 会带来同样的风险吗?
是的。CodePush 允许开发者将部署密钥替换为 sync({ deploymentKey: string }),这可能被恶意利用,以同样的方式接管应用安装。
🌐 Yes. CodePush allowed developers to swap deployment keys with sync({ deploymentKey: string }) which could be used maliciously take over an app installation in this same way.
示例代码
🌐 Example code
以下是如何使用这些 API 的示例:
🌐 Here's an example of how you might use these APIs:
import * as Updates from 'expo-updates'; // Where you call this method depends on your use case - it may make sense to // have a menu in your preview builds that allows testers to pick from available // pull requests, for example. function overrideUpdateURLAndHeaders() { Updates.setUpdateURLAndRequestHeadersOverride({ updateUrl: 'https://u.expo.dev/{updateId}/group/{groupId}', requestHeaders: {}, }); alert('Close and re-open the app to load the latest version.'); }
{ "expo": { "updates": { // We recommend only enabling this in preview builds. // You can use app.config.js to configure it dynamically. "disableAntiBrickingMeasures": true // etc. } } }