# Trusted Web Activity

> Trusted Web Activity 如何把 PWA 上架 Google Play，Digital Asset Links 验证如何决定是否显示地址栏，以及它与 WebAPK 的区别。

Trusted Web Activity（TWA）是一个 Android activity，在 Digital Asset Links 证明 APK 的签名密钥与网站属于同一方之后，用用户的 Chrome 全屏显示一个 web 源，不带地址栏。它是 PWA 上架 Google Play 的途径；Chrome 72 在 Android 上引入了它，该协议对其他浏览器开放，但 Chrome 的文档只记录了 Chrome 自身的支持。

## 工作原理

Android 应用本身不包含任何 web 内容。它的启动 activity（来自 `android-browser-helper` 库的 `com.google.androidbrowserhelper.trusted.LauncherActivity`）通过 Custom Tabs 服务绑定到用户的默认浏览器，请求以受信任模式打开 PWA 的 `start_url`，浏览器在自己的进程中渲染站点，使用的是用户的 Cookie、service worker 与存储。

信任在每次启动时建立一次：

1. 网站在 `/.well-known/assetlinks.json` 中以关系 `delegate_permission/common.handle_all_urls` 列出应用的包名与签名证书的 SHA-256 指纹。
2. APK 在 `asset_statements` 字符串资源中声明同一个源。
3. 启动时浏览器抓取 assetlinks 文件（以 `application/json` 提供、不经重定向即可访问）并比对指纹。匹配则隐藏地址栏；不匹配、文件缺失或发生重定向则回退为带地址栏的普通 Custom Tab，这是设计中的降级，而不是错误。

由于 Play 在 Play App Signing 下用自己的密钥为上传的应用签名，`assetlinks.json` 中的指纹必须是 Play 托管的签名密钥，而不是本地的上传密钥；这是生产环境中出现地址栏最常见的原因。

Android 上其他安装路径的区别在于由谁构建产物、能触达什么：

| | TWA | WebAPK（Chrome 安装） | 主屏幕快捷方式 |
|---|---|---|---|
| 分发方 | Google Play（也可 Galaxy Store、F-Droid） | Chrome 在安装时生成 | 任意浏览器 |
| 需要自己构建 Android 项目 | 是（Bubblewrap 或 Android Studio） | 否 | 否 |
| Digital Asset Links | 必需 | 不使用 | 不使用 |
| Play Billing | 是，通过 Digital Goods API | 否 | 否 |
| 更新 | web 内容即时生效；外壳需发布 Play 新版本 | 由 Chrome 重新检查 manifest | 无 |
| 运行于 | 用户的默认浏览器（若支持 TWA） | Chrome | 执行安装的浏览器 |

Play Billing 由网页通过 Digital Goods API（`window.getDigitalGoodsService('https://play.google.com/billing')`）配合 Payment Request 访问，仅在通过验证的 TWA 内可用；Play 政策要求应用内销售的数字商品走这条路。

## 示例

下面三个产物就是整个集成：站点上的 assetlinks 文件、由 manifest 生成的外壳，以及页面侧的检测。

### 发布 Digital Asset Links 声明

文件位于源的根目录；每个包名、每个签名密钥各一个数组项（调试密钥与发布密钥需要各自的指纹）。

```json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.app",
    "sha256_cert_fingerprints": [
      "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
    ]
  }
}]
```

Play Console 在"发布 › 设置 › 应用签名 › 应用签名密钥证书"下显示正确的指纹；用 `keytool` 对上传密钥生成的声明在本地通过，在 Play 分发的构建上失败。

### 用 Bubblewrap 生成并构建外壳

Bubblewrap 读取 web 应用 manifest，生成一个已填好包名、图标、颜色与 `asset_statements` 的 Android 项目。

```sh
npm i -g @bubblewrap/cli
bubblewrap init --manifest https://example.com/manifest.webmanifest
bubblewrap build          # 产出 app-release-signed.apk 与 app-release-bundle.aab
bubblewrap validate --url https://example.com/   # 基于 Lighthouse 的 PWA 检查
```

生成的项目锁定了 `android-browser-helper` 的版本；manifest 变化后用 `bubblewrap update` 重新生成。把 `.aab` 交付 Play 意味着 `assetlinks.json` 中的指纹必须如上所述使用 Play 的签名密钥。

### 检测页面是否运行在 TWA 内

浏览器会把文档的 referrer 设为启动它的 Android 包，这就是开启 Play 专属功能、隐藏 web 安装提示的信号。

```js
const fromTwa = document.referrer.startsWith('android-app://com.example.app/');

if (fromTwa && 'getDigitalGoodsService' in window) {
  enablePlayBilling();                      // 此处可用 Digital Goods API
} else {
  enableWebCheckout();                      // 其他地方走常规 web 支付
}
installButton.hidden = fromTwa || matchMedia('(display-mode: standalone)').matches;
```

referrer 只在会话的第一次导航中存在，所以要在应用内第一次导航把它清掉之前保存结果（例如存入 `sessionStorage`）；TWA 内 `display-mode: standalone` 同样为 `true`，但它无法区分 TWA 与 WebAPK。

:::observed
当 assetlinks 指纹与 APK 签名密钥不匹配时，应用打开后显示 Chrome 的 Custom Tab 工具栏与地址栏，而不是全屏，且不向用户显示任何错误；Chrome 的 TWA 概览记载了这一回退。在 Android 12 及以后，`adb shell pm get-app-links com.example.app` 打印声明的域名及其验证状态（`verified`、`none` 或一个旧式代码，见 Android App Links 验证指南）；服务器上文件正确却显示 `none`，说明设备在文件部署前就抓取过它，`adb shell pm verify-app-links --re-verify com.example.app` 可强制重新抓取。
:::

## 另请参阅

- [Trusted Web Activities overview](https://developer.chrome.com/docs/android/trusted-web-activity/overview)（developer.chrome.com）
- [Verify Android App Links](https://developer.android.com/training/app-links/verify-android-applinks)（developer.android.com）
- [Bubblewrap](https://github.com/GoogleChromeLabs/bubblewrap)（github.com）
- [Trusted Web Activity 浏览器支持](/zh/compatibility/twa/)
- [WebAPK](/zh/reference/installation/webapk/)
- [getInstalledRelatedApps()](/zh/reference/installation/get-installed-related-apps/)
- [分发与商店政策](/zh/ecosystem/distribution/)