Web In Play 的新动态

发布时间:2020 年 12 月 2 日

自 Trusted Web Activity 推出以来,Chrome 团队通过 Bubblewrap 简化了其使用方式。我们添加了更多功能,例如 Google Play 结算服务集成,并使其能够在更多平台(例如 ChromeOS)上运行。

Bubblewrap 和 Trusted Web Activity 功能

Bubblewrap 可帮助您创建在 Trusted Web Activity 中启动 PWA 的应用,而无需了解平台专用工具。

简化设置流程

以前,使用 Bubblewrap 需要手动设置 Java 开发套件和 Android SDK,这两者都很容易出错。现在,该工具在首次运行时会提供自动下载外部依赖项的选项。

如果您愿意,仍然可以选择使用现有依赖项安装,新的 doctor 命令有助于发现问题并推荐配置修复,现在可以使用 updateConfig 命令从命令行更新配置。

改进了向导

使用 init 创建项目时,Bubblewrap 需要相关信息来生成 Android 应用。该工具会从 Web 应用清单中提取值,并尽可能提供默认值。

您可以在创建新项目时更改这些值,但之前每个字段的含义并不明确。我们重新构建了初始化对话框,为每个输入字段提供了更好的说明和验证。

显示屏全屏和屏幕方向支持

在某些情况下,您可能希望应用尽可能多地使用屏幕,在构建 PWA 时,可以通过将 Web 应用清单中的 display 字段设置为 fullscreen 来实现此目的。

当 Bubblewrap 检测到 Web 应用清单中的全屏选项时,它会将 Android 应用配置为也以全屏模式启动,或以沉浸模式(Android 专用术语)启动。

Web 应用清单中的 orientation 字段用于定义应用应以人像模式、横屏模式还是设备当前使用的屏幕方向启动。Bubblewrap 现在会读取 Web 应用清单字段,并在创建 Android 应用时将其用作默认值。

您可以在 bubblewrap init 流程中自定义这两种配置。

AppBundles 输出

App Bundle 是一种应用发布格式,可将最终 APK 的生成和签名工作委托给 Play。在实践中,这使得用户从商店下载应用时,可以下载较小的文件。

Bubblewrap 现在会将应用打包为 App Bundle,并将其保存在名为 app-release-bundle.aab 的文件中。在向 Play 商店发布应用时,您应首选此格式,因为自 2021 年起,该商店要求使用此格式。

地理定位委托

用户希望安装在其设备上的应用具有一致的行为,无论采用何种技术。在 Trusted Web Activity 中使用时,GeoLocation 权限现在可以委托给操作系统,启用后,用户会看到与使用 Kotlin 或 Java 构建的应用相同的对话框,并在同一位置找到用于管理权限的控件。

该功能可通过 Bubblewrap 添加,由于它会向 Android 项目添加额外的依赖项,因此您应仅在 Web 应用使用地理定位权限时启用该功能。

优化后的二进制文件

在世界上的某些地区,存储空间有限的设备很常见,这些设备的所有者通常更喜欢较小的应用。使用 Trusted Web Activity 的应用会生成较小的二进制文件,从而消除这些用户的部分焦虑。

Bubblewrap 经过优化,减少了所需的 Android 库列表,从而使生成的二进制文件缩小了 800k。在实践中,这还不到之前版本生成的平均大小的一半。如需利用较小的二进制文件,您只需使用最新版本的 Bubblewrap 更新应用。

如何更新现有应用

由 Bubblewrap 生成的应用由一个 Web 应用和一个用于打开 PWA 的轻量级 Android 封装容器组成。尽管在 Trusted Web Activity 中打开的 PWA 与任何 Web 应用遵循相同的更新周期,但原生封装容器可以且应该更新。

您应更新应用,确保其使用最新版本的封装容器,并包含最新的 bug 修复和功能。安装最新版本的 Bubblewrap 后,update 命令会将最新版本的封装容器应用于现有项目:

npm update -g @bubblewrap/cli
bubblewrap update
bubblewrap build

更新这些应用的另一个原因是确保对 Web 清单所做的更改会应用于应用。为此,请使用新的 merge 命令:

bubblewrap merge
bubblewrap update
bubblewrap build

质量标准更新

Chrome 86 对 Trusted Web Activity 质量标准进行了更改,使用 Trusted Web Activity 的 PWA 的质量标准变更中对此进行了完整说明。

简而言之,您应确保应用能够处理以下场景,以防止其崩溃:

  • 应用启动时未能验证数字资产链接
  • 离线网络资源请求未能返回 HTTP 200
  • 应用中返回 HTTP 404 或 5xx 错误。

除了确保应用通过 Digital Asset Links 验证之外,其余场景均可由 Service Worker 处理:

self.addEventListener('fetch', event => {
  event.respondWith((async () => {
    try {
      return await fetchAndHandleError(event.request);
    } catch {
      // Failed to load from the network. User is offline or the response
      // has a status code that triggers the Quality Criteria.
      // Try loading from cache.
      const cachedResponse = await caches.match(event.request);
      if (cachedResponse) {
        return cachedResponse;
      }
      // Response was not found on the cache. Send the error / offline
      // page. OFFLINE_PAGE should be pre-cached when the service worker
      // is activated.
      return await caches.match(OFFLINE_PAGE);
    }
  })());
});

async function fetchAndHandleError(request) {
  const cache = await caches.open(RUNTIME_CACHE);
  const response = await fetch(request);

  // Throw an error if the response returns one of the status
  // that trigger the Quality Criteria.
  if (response.status === 404 ||
      response.status >= 500 && response.status < 600) {
    throw new Error(`Server responded with status: ${response.status}`);
  }

  // Cache the response if the request is successful.
  cache.put(request, response.clone());
  return response;
}

Workbox 融入了最佳实践,并消除了使用 Service Worker 时的样板代码。或者,考虑使用 Workbox 插件来处理这些情况:

export class FallbackOnErrorPlugin {
  constructor(offlineFallbackUrl, notFoundFallbackUrl, serverErrorFallbackUrl) {
    this.notFoundFallbackUrl = notFoundFallbackUrl;
    this.offlineFallbackUrl = offlineFallbackUrl;
    this.serverErrorFallbackUrl = serverErrorFallbackUrl;
  }

  checkTrustedWebActivityCrash(response) {
    if (response.status === 404 || response.status >= 500 && response.status <= 600) {
      const type = response.status === 404 ? 'E_NOT_FOUND' : 'E_SERVER_ERROR';
      const error = new Error(`Invalid response status (${response.status})`);
      error.type = type;
      throw error;
    }
  }

  // This is called whenever there's a network response,
  // but we want special behavior for 404 and 5**.
  fetchDidSucceed({response}) {
    // Cause a crash if this is a Trusted Web Activity crash.
    this.checkTrustedWebActivityCrash(response);

    // If it's a good response, it can be used as-is.
    return response;
  }

  // This callback is new in Workbox v6, and is triggered whenever
  // an error (including a NetworkError) is thrown when a handler runs.
  handlerDidError(details) {
    let fallbackURL;
    switch (details.error.details.error.type) {
      case 'E_NOT_FOUND': fallbackURL = this.notFoundFallbackUrl; break;
      case 'E_SERVER_ERROR': fallbackURL = this.serverErrorFallbackUrl; break;
      default: fallbackURL = this.offlineFallbackUrl;
    }

    return caches.match(fallbackURL, {
      // Use ignoreSearch as a shortcut to work with precached URLs
      // that have _WB_REVISION parameters.
      ignoreSearch: true,
    });
  }
}