Web In Play 的新动态

自从去年可信网络活动推出以来,Chrome 团队一直致力于 提升 Bubblewrap 的使用体验,还增添了一些新功能,例如即将推出的 Google Play 结算集成,并使其可在更多平台(例如 ChromeOS)上运行。本文将 总结 Trusted Web Activity 的最新和即将推出的更新。

新的 Bubblewrap 和 Trusted Web Activity 功能

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

简化的设置流程

以前,使用 Bubblewrap 需要手动设置 Java Development Kit 和 Android 两者都容易出错。该工具现在提供自动下载外部 依赖项。

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

改进的向导

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

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

display:支持全屏和屏幕方向

在某些情况下,您可能希望应用使用尽可能多的屏幕,并且当 构建 PWA 时,需要将 Web 应用清单中的 display 字段设置为 fullscreen

当 Bubblewrap 在 Web 应用清单中检测到全屏选项时,它会配置 Android 让应用同时以全屏模式(即沉浸模式)启动。

Web 应用清单中的 orientation 字段用于定义应用是否应在 竖屏模式、横屏模式或设备当前使用的屏幕方向。立即体验气泡膜 读取“Web 应用清单”字段,并在创建 Android 应用时将其用作默认字段。

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

AppBundle 输出

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 应用和一个轻量级 Android 设备组成 用于打开 PWA 的封装容器。尽管在 Trusted Web Activity 内打开的 PWA 会遵循 更新周期与任何 Web 应用相同,因此原生封装容器可以并且应该更新。

您应更新您的应用,确保它使用的是最新版本的封装容器, 问题修复和功能。安装最新版本的 Bubblewrap 后,update 命令将 将最新版本的封装容器应用于现有项目:

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

更新这些应用的另一个原因是,确保对网络清单的更改 应用状态为此,请使用新的 merge 命令:

bubblewrap merge
bubblewrap update
bubblewrap build

质量标准更新

Chrome 86 对“受信任的网络活动质量标准”进行了更改,具体请参见 请参阅更改采用 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,
    });
  }
}

Google Play 结算服务

除了允许您的应用在 Play 商店中销售数字商品和订阅内容外, Google Play 结算服务提供了用于管理目录、价格和订阅的工具, 报告,以及由用户熟悉的 Play 商店提供支持的结账流程。它 在 Play 商店中发布并销售数字商品的应用也需遵守这一要求。

Chrome 88 将在 Android 上推出源试用,支持集成 Trusted Web ActivityPayment Request APIDigital Goods API 通过 Google Play 结算服务实现购买流程。我们预计此源试用也会发布 适用于 ChromeOS 89 版本。

重要提示:Google Play Billing API 有自己的术语,其中包括客户端和 后端组件本部分只涵盖了 API 的一小部分,适用于使用 Digital Goods API 和 Trusted Web Activity。请务必阅读 查看 Google Play 结算服务文档,了解相关概念,然后再将其集成到 生产环境应用

基本流程

Play 管理中心菜单

要通过 Play 商店提供数字商品,您需要在 Play 上配置目录 商店,以及通过 PWA 将 Play 商店作为付款方式进行关联。

准备好配置目录后,请先找到左侧的“商品”部分 Play 管理中心的侧边菜单:

在这里,您可以找到用于查看现有应用内商品和订阅的选项,还可以 可以找到用于添加新广告单元的“创建”按钮

应用内商品

产品详情

如要创建新的应用内商品,您需要提供商品 ID、名称、说明和价格。时间是 创建有意义且好记的产品 ID 非常重要,您稍后会用到这些 ID 一经创建便无法更改

创建订阅时,您还必须指定结算周期。您可以选择 列出您的订阅权益,并添加一些功能,比如是否有免费试用、 初次体验价、宽限期和重新订阅选项。

创建完每个商品后,请将其激活,以便在您的应用中使用。

如果您愿意,可以通过 Play Developers API 添加商品。

配置目录后,下一步是通过 PWA 配置结账流程。您 将结合使用 Digital Goods APIPayment Request API 这个。

使用 Digital Goods API 提取商品价格

使用 Google Play 结算服务时,您需要确保向用户显示的价格 商品详情的价格手动保持这些价格同步是不可能实现的, Digital Goods API 为 Web 应用提供了一种查询基础付款的方式 提供价格:

// The SKU for the product, as defined in the Play Store interface
async function populatePrice(sku) {
  try {
    // Check if the Digital Goods API is supported by the browser.
    if (window.getDigitalGoodsService) {
      // The Digital Goods API can be supported by other Payments provider.
      // In this case, we're retrieving the Google Play Billing provider.
      const service =
          await window.getDigitalGoodsService("https://play.google.com/billing");

      // Fetch product details using the `getDetails()` method.
      const details = await service.getDetails([sku]);

      if (details.length === 0) {
        console.log(`Could not get SKU: "${sku}".`);
        return false;
      }

      // The details will contain both the price and the currenncy.
      item = details[0];
      const value = item.price.value;
      const currency = item.price.currency;

      const formattedPrice = new Intl.NumberFormat(navigator.language, {
        style: 'currency', currency: currency }).format(value);

      // Display the price to the user.
      document.getElementById("price").innerHTML = formattedPrice;
    } else {
      console.error("Could not get price for SKU \"" + sku + "\".");
    }
  } catch (error) {
    console.log(error);
  }
  return false;
}

您可以通过检查 getDigitalGoodsService() 是否为 适用于 window 对象。

然后,使用 Google Play 结算服务标识符作为参数来调用 window.getDigitalGoodsService()。 这将返回 Google Play 结算服务的一个服务实例,其他供应商可以实现支持 并且将具有不同的标识符。

最后,对 Google Play 结算服务对象的引用调用 getDetails(),并传递相应 SKU 作为参数。该方法将返回一个包含价格和 货币。

启动购买流程

Payment Request API 支持在网页上实现购买流程,也用于 Google Play 结算集成。如果您刚开始接触付款,请查看此 Payment Request API 的工作原理,了解详情 请求 API。

要将 API 与 Google Play 结算服务结合使用 有一个受支持的称为 https://play.google.com/billing 的方法,并将 SKU 添加为数据的一部分 乐器:

const supportedInstruments = [{
  supportedMethods: "https://play.google.com/billing",
  data: {
    sku: sku
  }
}];

然后,照常构建 PaymentRequest 对象并照常使用 API

const request = new PaymentRequest(supportedInstruments, details);

确认购买交易

交易完成后,您需要使用 Digital Goods API 确认 付款。PaymentRequest 中的响应对象将包含一个令牌,供您用于 确认交易:

const response = await request.show();
const token = response.details.token;
const service =
          await window.getDigitalGoodsService("https://play.google.com/billing");
await service.acknowledge(token, 'onetime');

Digital Goods API 和 Payment Request API 不知道用户的身份。作为 因此,你需要在后端将购买交易与用户关联起来 访问已购买的商品。将购买与用户关联时,别忘了保存 因为您可能需要使用该令牌来验证购买交易是否已取消或已退款 相应订阅仍然有效。请参阅 Real Time Developer Notifications APIGoogle Play Developer API,因为它们提供了用于在后端中处理这些情况的端点。

检查现有使用权

用户可能已经兑换了促销代码,或者可能已经订阅了您的商品。在 您可以调用 针对数字商品服务的 listPurchases() 命令。此操作会返回您在 客户在您的应用中做出了哪些贡献您还可以在这个位置确认任何未确认 购买交易,以确保用户正确兑换其使用权。

const purchases = await itemService.listPurchases();
for (p of purchases) {
  if (!p.acknowledged) {
    await itemService.acknowledge(p.purchaseToken, 'onetime');
  }
}

上传到 ChromeOS Play 商店

从 Chrome 85 开始,ChromeOS Play 商店也提供 Trusted Web Activity。流程 在 Chrome 操作系统中上架应用的方法与在 Android 设备上展示的方法相同。

创建 app bundle 后,Play 管理中心会引导您完成必需的操作 在 Play 商店中上架该应用的步骤。在 Play 管理中心文档中,您可以找到有关以下内容的帮助: 创建应用详情、管理 APK 文件和其他设置,以及相关说明 并安全地发布应用

若要将应用限制为仅在 Chromebook 上运行,请在初始化时添加 --chromeosonly 标志 在 Bubblewrap 中启动应用:

bubblewrap init --manifest="https://example.com/manifest.json" --chromeosonly

在不使用 Bubblewrap 的情况下手动构建应用时,请将 uses-feature 标志添加到您的 Android 清单:

<uses-feature  android:name="org.chromium.arc" android:required="true"/>

如果与 Android 应用分享商品详情,则 ChromeOS 专用软件包版本将始终显示 高于 Android 应用软件包的版本您可以将 ChromeOS 软件包版本设置为 因此您不必每次都同时更新两个版本 发布。