Web In Play 的新动态

发布时间:2020 年 12 月 2 日

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

Bubblewrap 和 Trusted Web Activity 功能

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

简化的设置流程

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

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

改进了向导

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

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

显示全屏和屏幕方向支持

在某些情况下,您可能希望应用尽可能使用屏幕的大部分区域,在构建 PWA 时,可以通过将网络应用清单中的 display 字段设置为 fullscreen 来实现这一点。

当 Bubblewrap 在网络应用清单中检测到全屏选项时,它会将 Android 应用配置为也以全屏(在 Android 中称为沉浸式模式)启动。

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

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

AppBundle 输出

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

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

地理位置委托

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

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

优化的二进制文件

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

通过缩减所需 Android 库的列表,我们优化了 Bubblewrap,生成的二进制文件缩小了 800KB。实际上,这比之前版本生成的平均大小还要小一半。如需利用较小的二进制文件,您只需使用最新版本的 Bubblewrap 更新应用即可。

如何更新现有应用

Bubblewrap 生成的应用由 Web 应用和用于打开 PWA 的轻量级 Android 封装容器组成。虽然在可信 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 验证之外,服务工作器还可以处理以下剩余场景:

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 内置了最佳实践,并在使用服务工件时移除了样板代码。或者,您也可以考虑使用 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,
    });
  }
}