首发于公众号 前端混合开发,欢迎关注。

更新移动应用程序的传统方法通常非常耗时,造成延迟,阻碍整体生产力。然而,React Native应用程序有一个显著的优势可以缓解这个问题: 无线更新(over-the-air updates),这是一个显著简化更新过程的特性。

在这篇文章中,我们将专注于实现OTA更新——特别是应用内更新——通过利用Expo应用服务(EAS)更新服务。我们将通过一些示例代码来更好地理解这是如何工作的,但请记住,这些代码是用来解释而不是一个完全功能的项目。

什么是无线更新?

无线更新,通常被称为OTA更新,允许开发者远程更新应用,无需经过传统的应用商店更新过程。OTA更新方法带来了几个好处,如快速的响应时间和改善的用户体验。

开发者可以迅速解决关键错误或安全漏洞,并直接向用户推送更新。最好的部分?用户无需手动更新,确保他们始终可以使用最新的功能和改进。

OTA更新的两个突出方面是CodePush和应用内更新。在本教程中,我们将使用应用内更新,所以让我们开始探索这意味着什么。

什么是应用内更新?

应用内更新是指移动应用的能力,可以提示用户直接从应用内部下载和安装更新,或者静默地进行小修复,而不是将用户重定向到外部应用商店。目标是为更新应用提供无缝且用户友好的体验。

通常有两种类型的应用内更新:

  • 即时更新:这要求用户立即安装更新,可能会打断他们当前的应用程序会话
  • 灵活更新:这允许用户在后台下载更新时继续使用应用。当方便时,用户会被提示安装更新。

你使用的类型可能取决于你的更新的优先级。对于影响应用的安全性和功能的关键更新或突发变化,立即更新是最好的。灵活的更新较为不显著,使它们对于小的功能、性能和美学改进非常有用。

为什么OTA很重要?

如果你有部署 Web 应用程序的经验,你就会知道与 Web 相比,移动应用部署的复杂性要高得多。即使只是一个打字错误这样小的问题,开发人员也必须经历整个部署过程,包括等待 App Store 或 Google Play 的审批。

移动应用商店团队需要确保你的产品符合他们的政策和最佳实践。这个评估过程在苹果生态系统中提出了特定的挑战,在那里应用可能因为不符合政策或未能满足UI标准而被移除或拒绝。

因此,提交过程非常耗时。但是,在准备发布关键更新时,每一分钟都变得至关重要。

这些就是OTA旨在解决的问题。我们可以让用户无需等待漫长的审查过程就能获取更新。
使用React Native也可以最小化被拒绝的风险,特别是因为开发主要围绕应用程序的JavaScript部分。React Native核心团队确保对框架所做的任何修改都对成功提交你的应用程序的影响最小。

OTA禁令历史

通过 CodePush,你可以直接将代码推送到用户设备,这使你可以绕过应用商店的审核过程。这导致过去常有一种误解,认为苹果禁止使用CodePush的某些类型的应用。

实际上,苹果拒绝使用CodePush的应用程序的原因是它们没有遵循更新应用程序的指南。苹果开发者计划许可协议的第3.3.1段明确规定了苹果的指南:

"除下一段规定外,应用程序不得下载或安装可执行代码。解释型代码可以下载到应用程序中,但前提是此类代码:(a)不会通过提供与提交到App Store的应用程序的预期和公布目的不一致的功能或特性来改变应用程序的主要目的,(b)不会为其他代码或应用程序创建商店或店面,以及(c)不会绕过操作系统的签名、沙箱或其他安全功能。"

这简单地意味着,只要你执行JavaScript和资源的无线更新,你的应用程序就不会被拒绝。

理解EAS更新服务

EAS Update是Expo提供的一项服务,它允许开发者使用 expo-updates 库在他们的应用中实现OTA更新。这种方便的CodePush替代方案使您能够更新现有的应用,而无需构建和提交一个全新版本到App Store。

EAS Update通过自动更新你的应用程序来简化流程。它甚至适用于原生React Native应用程序 - 唯一的前提条件是安装 expo ,一个轻量级的npm包。

EAS更新是如何工作的?

应用程序被分为两层:

image.png

原生层被构建到你的应用程序的二进制文件中,包括应用图标、应用配置、二进制文件内的原生代码等。更新层包括你在进行更新时可以替换的应用程序部分,例如JavaScript和TypeScript代码、图像资产等。

如前所述,我们只能对应用程序的更新层进行更新。当进行更新时,库会在打开应用程序时启动更新检查。

  • 如果检测到比当前运行的更新更新的更新,库将开始下载并执行较新的更新
  • 在没有新的更新时,库会回退到构建应用时内嵌的更新

更新检查和下载的时间是可自定义的。

在一个整合了 expo-updates 服务的Expo项目中,项目中包含的原生Android和iOS代码负责监督、获取、解析和验证更新。

expo-updates 执行更新采用两阶段下载过程。在第一阶段,它会获取最新的更新清单。这个清单包含了关于更新的信息,包括一份所需资产的列表,如图片、JavaScript包和字体文件。

随后,在第二阶段,库会下载清单中指定的尚未从之前的更新中获取的资产。例如,如果更新引入了一张新图片,库会在实施更新之前获取新的图片资产。

如果库成功获取了清单(第一阶段)和所有必要的资产(第二阶段)在指定的 fallbackToCacheTimeout 设置内,新的更新将在应用程序启动时立即执行。

在库无法在 fallbackToCacheTimeout 期间检索清单和资源的情况下,它会在后台继续下载新的更新,并在后续的应用程序启动期间应用它。

为一个演示用的 React Native 应用实现应用内更新

在演示React Native应用程序中实现应用内更新

我们使用Expo构建一个示例的 React Native 应用程序,演示如何实现 eas-updates 。第一步是确保我们已经全局安装了 eas-cli

npm install -g eas-cli

eas-cli 是Expo提供的一个工具,用于帮助我们管理应用程序,从处理构建到将应用提交到应用商店。确保登录到你的EAS账户:

eas login

接下来,我们需要安装EAS更新库并配置我们的项目:

npx expo install expo-updates

现在,请导航到位于应用程序根目录的 app.json 文件。添加一个带有 expo-updates 插件的 plugins 键,并在配置中包含你的Expo用户名。你可以在登录 Expo 后获取你的用户名:

// app.json

"plugins": [
  [
    "expo-updates",
    {
      "username: "account username"
    }
  ]
]

接下来,我们用 eas update 初始化我们的项目:

eas update:configure

之后,你应该会在终端中看到如下所示的消息:

image.png

现在我们准备配置我们的构建:

# Set up the configuration file for builds
- eas build:configure

执行上述命令将在你的项目根目录中创建一个 eas.json 文件。我们可以在那里添加一些配置:

{
  "build": {
    "preview": {
      "channel": "staging"
      // ...
    },
    "production": {
      "channel": "production"
      // ...
    }
  }
}

对于每个构建配置文件 —— stagingproduction,我们添加了一个 channel 属性。这个属性有助于将更新指向与特定配置文件相关联的构建。

那么,为什么我们需要设置这个呢?我们考虑一个场景,你设置了一个GitHub Action,在合并到 production Git分支时发布更改:

  • 在生产构建配置文件中,将 channel 属性设置为 production 后,每次提交到 production 分支都会触发 GitHub Action。
  • 这反过来会发布一个更新,该更新可以被与 production 频道相关联的构建访问
  • channel 充当链接机制,确保根据指定的配置文件将更新定向到适当的构建

到此为止,我们的设置应该已经完成。现在,在你的 app.js 文件中,从 expo-updates 包中导入 updates 函数,并将文档中的 onFetchUpdateAsync 函数复制到你的文件中:

import * as Updates from 'expo-updates';

function App() {
  async function onFetchUpdateAsync() {
    try {
      const update = await Updates.checkForUpdateAsync();

      if (update.isAvailable) {
        await Updates.fetchUpdateAsync();
        await Updates.reloadAsync();
      }
    } catch (error) {
      alert(`Error fetching latest Expo update: ${error}`);
    }
  }

  useEffect(() => {
    onFetchUpdateAsync()
  }, [])

  // rest of your code
}

我们在 useEffect 钩子中调用那个函数,以便每次应用程序加载时检查更新。请注意,这只是一个基本的实现。如果你想做一些像向用户显示一个通知他们更新的模态框的事情,你也可以将更新逻辑抽象成一个钩子,然后在你的 app.js 文件中使用它。

image.png

现在,你已经准备好使用 EAS Update 进行后续的更改了。然而,在我们可以进行后续的更新之前,我们需要注意一件事,那就是始终更新我们的应用版本号。

  • 这是什么意思:将应用版本号视为你的应用构建模块的兼容性标签
  • 为什么这很重要:应用版本号确保更新与你的应用的基础结构相匹配,防止崩溃

app.json 文件中,我们有一个 version 键。当我们进行新的更新时,可以在该键中更新我们的应用版本。版本应遵循语义化版本(SemVer)约定来传达变更的性质:

我们已经成功地在我们的应用程序中设置了 eas update 来在应用加载时检查更新。现在,让我们看看如何发布我们的更新。

发布我们的 React Native 应用的更新

当我们进行更新时,我们可以像这样发布该更新:

eas update --branch [your branch name] --message [message]

eas update --branch staging --message "fix typo"

以下是上述代码的分解:

  • eas update : 启动更新过程
  • --branch staging :此标志指定了你想要触发更新的分支。在我们的情况下,它被设置为 staging ,表明这个更新是为 staging 分支准备的。
  • --message "fix typo" :此标志允许你包含一条描述更新相关的目的或变更的信息

这个Expo指南演示了如何在遵循原生React Native工作流的项目中复制相同的过程。

在 React Native 中实现应用内更新的最佳实践

在实施应用内更新时,有几件事情你应该始终注意。

在发布任何更新之前,要在各种设备和平台上进行彻底的测试,以保证用户体验的顺畅和无错误。用户满意度取决于你的应用程序的可靠性。

如果更新导致了无法预见的问题,有一个回滚计划至关重要。使用户能够回退到之前的版本,直到成功解决任何遇到的问题。

最重要的是,通过应用内通知或弹窗,让你的用户了解最新的更新。让他们了解新的功能和改进。建立透明度可以增进信任,并鼓励用户积极参与。

总结

通过无线更新直接将JavaScript变更动态发送给用户,可以让你跳过App Store和Google Play的严格审核流程。这使你能够立即为所有用户更新应用程序,大大减少了通常需要的时间。

多年来,官方应用商店的审核时间已经逐渐改善。即便如此,能够推出 OTA 更新仍是一项宝贵的应急计划,特别是在迅速解决从测试流程中漏掉并进入生产环节的错误这样的情况时。

采用支持 OTA 的实践方式可以提供更敏捷和响应式的错误解决方法,从而缩短恢复时间。如果你对于在 React Native 应用中实施应用内更新还有任何疑问,欢迎在下方留言提问。

交流

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68.1k 声望105k 粉丝