运行时覆盖更新配置
编辑页面
了解如何在运行时覆盖更新 URL 和请求头,以控制客户端加载哪个更新。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
使用 EAS Update 的典型方式是在你的应用构建中嵌入单一的更新 URL 和一组请求头(例如更新通道名称)。要控制加载哪个更新,你可以通过 eas update 命令或 EAS 控制台在服务器端进行更改。例如,你将一个新更新发布到你的构建所指向的通道,然后该构建会在下次启动时获取该更新。使用这种方法,发布到与你的构建所指向通道不同的更新将不会被下载。
本指南解释了如何在运行时更改更新 URL 和请求头,从而可以通过 ID 加载特定更新,或者更改从哪个通道拉取更新,而无需创建并安装新的构建。
覆盖请求头
本节描述的功能可在 Expo SDK 54 中使用,且需要expo-updates0.29.0 及更高版本。
此功能的主要用例是 channel surfing,它允许在生产构建中于运行时切换更新通道。这使得非技术相关方能够测试和验证进行中的更新(例如来自拉取请求或不同功能分支的更新),而无需使用开发构建或为每次更改创建单独的预览构建。
例如,如果你有一个用于生产更新的 default 通道,以及一个用于预览更新的 preview 通道,你可以覆盖 expo-channel-name 请求头,使其指向 preview 通道。这样就可以在当前生产构建中测试预览更新。
另一种潜在用例是向不同用户提供不同更新,例如让一组内部用户(如员工)在最终用户之前先接收更新。
什么是 channel surfing?
Channel surfing 是指在运行时切换已安装应用从哪个更新通道拉取更新。运行中的应用不再永久绑定到构建时配置的通道,而是可以被重定向到另一个通道(例如 preview 通道)并从那里加载更新。
你可以使用 channel surfing 来:
- 允许单个已安装应用按需在通道之间切换。应用可以在运行时切换通道,而不是永久锁定到构建时定义的通道。
- 在真实构建上启用预览和测试流程,使开发者、QA 或其他相关方能够使用用户已安装的同一个生产构建来尝试进行中的更新。
- 加快迭代和验证:通过将应用重定向到另一个通道,可以立即测试、审查或验证更新,而无需等待新的构建。
有关 channel surfing 所解决的问题以及如何应用它的更多信息,请阅读 关于 channel surfing 的博客文章。
工作原理
你可以通过调用 Updates.setUpdateRequestHeadersOverride 来覆盖 expo-channel-name 请求头。这将覆盖更新请求,使其从指定通道获取更新。
在应用中的某个位置,你需要提供一种方式让用户触发请求头的更改。这可以是只有受信任用户才能访问的隐藏菜单,或其他机制,具体取决于你的用例。参数更改后,你可以调用 fetchUpdateAsync() 获取更新,然后调用 reloadAsync() 重新加载应用。或者你也可以等待下一次启动,应用会自动获取并安装更新。
import * as Updates from 'expo-updates'; // 你在何处调用此方法取决于你的用例——例如,在预览构建中提供一个菜单,让测试人员从可用通道中进行选择,可能会很合适: Updates.setUpdateRequestHeadersOverride({ 'expo-channel-name': 'preview' }); // 你可以立即获取并重新加载更新,或者等待下次启动 await Updates.fetchUpdateAsync(); await Updates.reloadAsync();
你希望在运行时覆盖的任何请求头,都必须在应用配置中的updates.requestHeaders中声明。
切换通道时的风险和注意事项
切换通道会改变应用运行的 JavaScript bundle。如果你的应用依赖于在不同通道之间不兼容的迁移或数据结构,那么来回切换可能会导致问题。
例如,如果某个 beta 更新应用了数据库迁移,生产版本可能无法理解新的 schema。开发者应确保他们的更新在通道之间切换时保持安全,或者在需要时限制只允许单向切换。
同时覆盖更新 URL 和请求头
本节描述的功能可在 Expo SDK 52 中使用,且需要expo-updates0.27.0 及更高版本。对于生产应用,不建议使用disableAntiBrickingMeasures选项,它当前主要面向预览环境。
与 覆盖请求头 类似,如果你想进一步将更新 URL 覆盖为某个特定更新,可以使用 Updates.setUpdateURLAndRequestHeadersOverride 方法。这使你即使在当前构建创建之前发布的更新,也能按 ID 加载特定更新。
在决定在生产环境中使用此功能之前,务必要熟悉 安全注意事项。未来我们可能会增加对该功能更受限版本的支持,以更适合此类用例。
工作原理
有两个相关 API:
Updates.setUpdateURLAndRequestHeadersOverride({ url: 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({ url: 'https://u.expo.dev/{updateId}/group/{groupId}', requestHeaders: {}, }); alert('关闭并重新打开应用以加载最新版本。'); }
{ "expo": { "updates": { // 我们建议仅在预览构建中启用此项。 // 你可以使用 app.config.js 动态配置它。 "disableAntiBrickingMeasures": true // 等等.. } } }