# related_applications 与 prefer_related_applications

> related_applications 按 platform、url 与 id 列出 PWA 的原生或 Web 对应应用；prefer_related_applications 让 Android 上的 Chrome 改为推荐其中一个。

`related_applications` 是一个对象数组，每一项给出 `platform` 以及商店 `url` 或 `id`，指向这个 Web
应用的原生或 Web 对应版本。`prefer_related_applications` 是布尔值，告诉浏览器推荐其中一个对应应用，
而不是提议安装 Web 应用本身。`navigator.getInstalledRelatedApps()` 在运行时读取的也是同一个数组。

Android 上的 Chrome 44、Samsung Internet 4.0、Android WebView 44 与 Edge 17 处理这两个成员（BCD
`html.manifest.related_applications`）。桌面端 Chrome 在 Windows 上为 `getInstalledRelatedApps()`
读取数组，但对 `prefer_related_applications` 只做一件事：值为 `true` 时拒绝安装。Firefox 157 解析
这两个成员后不做处理；Safari 27 不读取它们。

## 成员

- **类型**：`related_applications` 是对象数组，字段有 `platform`（字符串，必填）、`url`（字符串）、
  `id`（字符串）、`min_version`（字符串）与 `fingerprints`（`{ type, value }` 数组）。`url` 与 `id`
  至少要有一个。`prefer_related_applications` 是布尔值。
- **允许的 `platform` 值**：`play`（Google Play）、`itunes`（App Store）、`windows`（Microsoft
  Store）、`chrome_web_store`、`f-droid`、`amazon`，以及 `webapp`（以清单 `id` 标识的 PWA）。
- **默认值**：空数组，以及 `false`。省略 `prefer_related_applications` 等同于写 `false`。
- **示例值**：`[{ "platform": "play", "id": "com.example.app" }]` 搭配
  `"prefer_related_applications": true`。

Chromium 逐条校验：缺少 platform 的条目记录
`'platform' is a required field, related application ignored.`，既没有 `url` 也没有 `id` 的条目记录
`one of 'url' or 'id' is required, related application ignored.`；其余条目保留。这种关系是单向的：原生应用不需要反过来指向 Web 应用，唯一的
例外是 `getInstalledRelatedApps()`，它在 Android 上会先核验 Play 应用的 Digital Asset Links 声明，
再报告其已安装（[Digital Asset Links](https://developers.google.com/digital-asset-links/v1/getting-started)，developers.google.com）。

`prefer_related_applications: true` 在 Chromium 里有两种效果。在 Chrome for Android（仅 Beta 与
Stable 渠道）上，安装提示改为推荐第一个 `play` 条目而不是 Web 应用。在其他所有地方它会阻断可安装性：
可安装性检查报告 `The manifest specifies prefer_related_applications: true`，在其他渠道或平台上还会
附加 `Manifest 'prefer_related_applications' is only supported on Chrome Beta and Stable channels on
Android.`。

:::observed
Chrome 155（macOS 26，英文界面）的 DevTools > Application > Manifest：清单含
`"prefer_related_applications": true` 时，**Installability** 一节显示
`The manifest specifies prefer_related_applications: true`，地址栏的安装图标不出现。写成
`{ "url": "https://play.google.com/store/apps/details?id=com.example.app" }` 而没有 `platform` 的
条目会在 **Errors and warnings** 下增加 `'platform' is a required field, related application ignored.`。
:::

## 示例

第一个示例是两个成员所在的清单；后两个是页面在运行时如何使用这个数组。

### 指向 Play 商店应用与 Microsoft Store 页面

一个有原生同胞的 Web 应用把两者都列出来。Play 条目以包名作 `id`、以商店页面作 `url`；Microsoft
Store 条目只有 `url`。不写 `prefer_related_applications` 时，Web 应用仍可安装，这个列表只服务于
`getInstalledRelatedApps()`。

```json
{
  "name": "Trail Maps",
  "start_url": "/",
  "display": "standalone",
  "related_applications": [
    {
      "platform": "play",
      "url": "https://play.google.com/store/apps/details?id=com.example.trailmaps",
      "id": "com.example.trailmaps"
    },
    {
      "platform": "windows",
      "url": "https://apps.microsoft.com/store/detail/trail-maps/9WZDNCRFHVJL"
    }
  ]
}
```

只有当应用在 Android 上绝对不能以 PWA 形式安装时才加 `"prefer_related_applications": true`；它会
同时移除所有其他 Chromium 平台上的 PWA 安装路径。

### 原生应用已安装时隐藏安装按钮

`navigator.getInstalledRelatedApps()` 以数组中已安装在本设备上的条目兑现（Android 上的 Chrome 80；
Windows 上的 Chrome 85 支持 `windows` 条目）。它只能在应用自身源的顶层框架中调用，且 Play 应用必须
为该源发布 Digital Asset Links 声明。回退分支为没有该方法的浏览器保留按钮。

```js
async function nativeAppInstalled() {
  if (typeof navigator.getInstalledRelatedApps !== 'function') {
    return false; // Safari、Firefox、iOS 版 Chrome：展示 Web 安装路径
  }
  try {
    const apps = await navigator.getInstalledRelatedApps();
    return apps.some((app) => app.platform === 'play' || app.platform === 'windows');
  } catch {
    return false; // 不在顶层框架，或 asset links 校验未通过
  }
}

nativeAppInstalled().then((installed) => {
  document.querySelector('#install-web-app').hidden = installed;
});
```

兑现的对象带有 `platform`、`url`、`id` 与 `version`，所以设置了 `min_version` 时，同一个调用还能
发现过时的原生应用。

### 检测 PWA 自身是否已经安装

`id` 等于清单自身 `id` 的 `webapp` 条目，让在浏览器标签页里打开的站点能询问本设备是否已安装它的
PWA。Chrome 85 及之后会兑现这一查询；其他浏览器落到 display-mode 检查，而后者只知道当前窗口的
情况。

```json
{
  "id": "/",
  "related_applications": [
    { "platform": "webapp", "url": "https://trailmaps.example/manifest.webmanifest" }
  ]
}
```

```js
async function pwaInstalled() {
  if (matchMedia('(display-mode: standalone)').matches) return true; // 正以应用形式运行
  if (typeof navigator.getInstalledRelatedApps !== 'function') return false;
  const apps = await navigator.getInstalledRelatedApps();
  return apps.some((app) => app.platform === 'webapp');
}
```

用这个结果把"安装"号召改成"打开应用"；`webapp` 条目对可安装性和安装提示没有任何影响。

## 另请参阅

- [getInstalledRelatedApps()：检测已安装的原生或 PWA 对应应用](/zh/reference/installation/get-installed-related-apps/)
- [id 清单成员](/zh/reference/manifest/id/)
- [可安装性条件：PWA 如何才能被安装](/zh/reference/installation/installability-criteria/)
- [Trusted Web Activity（TWA）：PWA 进入 Play 商店](/zh/reference/installation/twa/)
- [Manifest Incubations: related_applications member](https://wicg.github.io/manifest-incubations/#related_applications-member)（wicg.github.io）
- [Navigator: getInstalledRelatedApps() method](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps)（developer.mozilla.org）