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 Update 的典型方式是在应用构建中嵌入单一的更新 URL 和一组请求头(例如更新通道名称)。要控制加载哪个更新,你可以通过 eas update 命令或 EAS 控制台在服务器端进行更改。例如,你可以将一个新更新发布到构建所指向的通道,然后该构建会在下次启动时获取该更新。使用这种方法,发布到与构建所指向通道不同的更新将不会被下载。
本指南解释了如何在运行时更改更新 URL 和请求头,从而可以通过 ID 加载特定更新,或者更改拉取更新的通道,而无需创建并安装新的构建。
覆盖请求头
本节描述的功能可在 Expo SDK 54 中使用,且需要
expo-updates0.29.0 及更高版本。
对于使用 EAS Update 的应用,此功能的主要用例是 通道切换。通道切换会在完整的通道切换流程中,在运行时切换 expo-channel-name 请求头。
例如,如果你有一个用于生产更新的 default 通道,以及一个用于预览更新的 preview 通道,你可以覆盖 expo-channel-name 请求头,使其指向 preview 通道。这样就可以在当前生产构建中测试预览更新。
切换通道可能会导致兼容性问题,尤其是在更新使用不同的迁移方式或数据结构时。请参阅切换通道时的风险和注意事项。
另一个潜在的用例是向不同用户提供不同的更新,例如,让一组内部用户(如员工)比最终用户更早收到更新。
工作原理
你可以通过调用 Updates.setUpdateRequestHeadersOverride 来覆盖请求头。对于 EAS Update,覆盖 expo-channel-name 请求头会使应用从指定通道请求更新。
要在运行时覆盖 expo-channel-name 请求头,原生构建必须包含该请求头。当 eas.json 中的构建配置文件定义了 channel 时,EAS Build 会自动将该请求头设置为对应的值。在 EAS Build 之外创建的构建(例如使用 npx expo run:android 创建的构建)不会自动设置该请求头。对于这些构建,请在应用配置中的 updates.requestHeaders 中声明该请求头,然后重新构建应用。
在应用中提供一种让用户触发请求头更改的方式。这可以是只有受信任用户才能访问的隐藏菜单,也可以是其他适合你用例的机制。更改请求头后,调用 fetchUpdateAsync() 获取更新,并调用 reloadAsync() 重新加载应用。你也可以等待下次启动,以自动获取并安装更新。
import * as Updates from 'expo-updates'; // 你在何处调用此方法取决于你的用例——例如,在预览构建中提供一个菜单,让测试人员从可用通道中进行选择,可能会很合适: Updates.setUpdateRequestHeadersOverride({ 'expo-channel-name': 'preview' }); // 你可以立即获取并重新加载更新,或者等待下次启动 await Updates.fetchUpdateAsync(); await Updates.reloadAsync();
我可以覆盖其他请求头吗?
可以。你希望在运行时覆盖的任何其他请求头,都必须在应用配置中的 updates.requestHeaders 中声明。你传递给 setUpdateRequestHeadersOverride() 的对象会替换构建中的所有自定义请求头,因此在覆盖生效期间,请包含应用仍然需要的每个请求头。
同时覆盖更新 URL 和请求头
本节描述的功能可在 Expo SDK 52 中使用,且需要
expo-updates0.27.0 及更高版本。在预览环境中使用disableAntiBrickingMeasures选项。避免在生产应用中使用。
与覆盖请求头类似,如果你想进一步将更新 URL 覆盖为某个特定更新,可以使用 Updates.setUpdateURLAndRequestHeadersOverride 方法。这使你即使在当前构建创建之前发布的更新,也能按 ID 加载特定更新。
在决定在生产环境中使用此功能之前,务必要熟悉安全注意事项。未来我们可能会增加对该功能更受限版本的支持,以更适合此类用例。
工作原理
有两个相关 API:
Updates.setUpdateURLAndRequestHeadersOverride({ updateUrl: string, requestHeaders: Object })- 此方法会覆盖 app.json/Expo.plist/AndroidManifest.xml 中指定的更新 URL 和请求头,例如expo-channel-name请求头。disableAntiBrickingMeasures- 应用配置中的此字段会禁用expo-updates内置的防变砖措施,这些措施可确保始终能够发布后续更新,以修复之前安装的更新中存在的问题。当你更改此值时,需要创建新的构建才能使其生效。不要在生产构建中启用此项。 之所以使用这个名称,是为了明确表明,当你覆盖更新 URL/请求头后,我们将无法再安全地回滚到之前加载的更新。因此,如果你加载的新更新导致应用崩溃,expo-updates将无法自动恢复,因为此字段与setUpdateURLAndRequestHeadersOverride结合使用后会禁用嵌入式更新,因此将没有任何可回滚的更新。用户需要卸载并重新安装应用。你应该仅在预览构建中使用此功能。
如何使用这些 API:
- 覆盖更新 URL/请求头,并提示用户关闭应用:在应用中的某个位置,你需要提供一种方式让用户触发对 URL 和/或请求头的更改。这可以是只有受信任用户才能访问的隐藏菜单,或其他机制,具体取决于你的用例。参数更改后,通过提示框等方式通知用户需要关闭并重新打开应用。
expo-updates库中的方法,例如checkForUpdateAsync(),在应用关闭并重新打开之前,不会使用新的已覆盖 URL 和请求头。 - 新更新将在下次打开应用时下载并启动:当应用完全关闭(“杀死”状态,而不仅仅是进入后台)并重新打开后,更新及其相关资源都会被下载。准备就绪后,应用将启动。在下载期间,用户需要等待启动画面。我们理解在启动画面等待并不理想,如果此功能被广泛使用,我们计划在未来改进这一体验。对于当前推荐的用例(预览),这可能是一个可接受的折中方案。
安全注意事项
可以通过 disableAntiBrickingMeasures 禁用的防变砖措施可确保无论发布了什么更新,你之后总能再发布另一个更新并让其生效。禁用防变砖措施后,某些类别的攻击和利用会变得可行,尤其是在内部(被入侵的员工)发布恶意更新方面。例如,拥有发布更新能力的员工可以发布一个恶意更新,将更新 URL 和请求头改为指向他们自己的服务器,从而接管该应用的安装。通过对生产更新使用代码签名并限制密钥访问,可以缓解但不能消除这一风险。
CodePush 的类似用法是否也有同样的风险?
是的。CodePush 允许开发者使用 sync({ deploymentKey: string }) 交换部署密钥,这同样可以被恶意利用,以这种方式接管应用安装。
示例代码
下面是一个你可以如何使用这些 API 的示例:
import * as Updates from 'expo-updates'; // 你在何处调用此方法取决于你的用例——例如,在预览构建中提供一个菜单,让测试人员可以从可用的 // 拉取请求中进行选择,可能会很合适。 function overrideUpdateURLAndHeaders() { Updates.setUpdateURLAndRequestHeadersOverride({ updateUrl: 'https://u.expo.dev/{updateId}/group/{groupId}', requestHeaders: {}, }); alert('关闭并重新打开应用以加载最新版本。'); }
{ "expo": { "updates": { // 我们建议仅在预览构建中启用此项。 // 你可以使用 app.config.js 动态配置它。 "disableAntiBrickingMeasures": true // etc. } } }