3. Universal Links与App Links原理

Universal Links(iOS)与App Links(Android)是两大移动平台提供的“标准深链”能力,它们的共同目标是:让一个普通的 https:// 链接在已安装对应 App 时直接唤起 App,在未安装时优雅地回退到网页。二者表面行为相似,但底层验证机制、关联文件格式、系统匹配流程存在本质差异。理解这些差异,是排查“链接不跳转”“跳转不稳定”“首次点击失效”等生产问题的前提。

一、共同的设计哲学:用 HTTPS 域名做信任锚点

传统自定义 Scheme(如 myapp://)存在两个致命问题:任何 App 都能声明同一个 Scheme,导致劫持;浏览器和系统无法判断链接归属,常弹出“是否打开某 App”的确认框。Universal Links 与 App Links 的核心思路是:把 App 与一个你拥有控制权的 HTTPS 域名绑定起来。系统通过访问该域名下的一个约定文件,验证“这个域名确实授权了这个 App”,从而建立信任。链接本身仍是标准 HTTPS URL,因此可被搜索引擎索引、可在网页中正常打开,也能被系统安全地交给 App 处理。

二、iOS Universal Links:apple-app-site-association 与系统验证

iOS 的关联文件名为 apple-app-site-association(简称 AASA),无扩展名,必须通过 HTTPS 直接访问,且 Content-Type 应为 application/json,不能有重定向。它可放在域名根目录 https://example.com/apple-app-site-association,也可放在 /.well-known/ 下。文件结构包含 applinks 字段,其下 details 数组为每个 App 指定 appID(格式为 TeamID.BundleID)和 paths(或新版 components)白名单。

{
  "applinks": {
    "details": [
      {
        "appID": "9JA89QQLNQ.com.example.app",
        "components": [
          { "/": "/product/*", "comment": "商品详情页" },
          { "/": "/order/*", "exclude": true, "comment": "订单页不接管" }
        ]
      }
    ]
  }
}

验证时机上,iOS 并非在每次点击时实时请求 AASA,而是在App 安装/更新时由系统(通过 swcd 进程)拉取并缓存,同时受 Apple CDN 缓存影响。这意味着:修改 AASA 后,已安装用户不会立即生效,通常需要重装 App 或等待系统刷新。这也是“配置改了但线上不生效”的最常见原因。此外,AASA 文件大小建议不超过 128KB,路径匹配区分大小写,查询参数默认不参与匹配。

三、Android App Links:assetlinks.json 与数字资产链接

Android 的关联文件为 assetlinks.json,必须放在 https://example.com/.well-known/assetlinks.json,同样要求 HTTPS、无重定向、Content-Type: application/json。其核心是声明“域名与 App 的签名证书指纹”之间的绑定关系,即 Digital Asset Links。

[
  {
    "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"
      ]
    }
  }
]

与 iOS 不同,Android 的验证发生在安装时和运行时:系统在安装 App 后会尝试校验,用户点击链接时若尚未验证,系统会实时发起校验请求。验证通过后,该域名下的链接默认直接进入 App,不再弹出选择框。关键点在于 sha256_cert_fingerprints 必须与实际签名证书一致——使用 Google Play App Signing 时,要填 Play 控制台里的“应用签名证书”指纹,而不是本地上传密钥的指纹,这是极高频的踩坑点。此外,Android 12 起对 android:autoVerify="true" 的 intent-filter 要求更严格,未声明 autoVerify 的链接不会自动验证。

四、系统匹配流程对比

下图对比了两平台从“用户点击链接”到“决定由谁处理”的关键路径与验证时机差异。

Universal Links 与 App Links 匹配流程对比 用户点击 https:// 链接 iOS · Universal Links 安装/更新时系统拉取 AASA apple-app-site-association(无扩展名) 校验 TeamID.BundleID 与 paths 结果缓存于系统,受 CDN 影响 点击时查本地缓存判定归属 命中则直接唤起 App 未命中 → 浏览器打开网页 无选择弹窗,体验平滑 Android · App Links 安装时/运行时校验 assetlinks .well-known/assetlinks.json 比对包名与签名证书指纹 sha256_cert_fingerprints 验证通过则默认直接进 App 需 autoVerify="true" 未验证 → 可能弹选择框或走网页 可实时补校验 核心差异:iOS 验证时机偏“安装期 + 缓存”,Android 偏“安装期 + 点击时实时校验”

五、平台差异与生产要点

把差异落到工程实践上,可以归纳为几个必须记住的判断依据。第一,验证时机不同:iOS 依赖安装期缓存,AASA 变更后老用户不重装往往不生效;Android 在点击时会实时补校验,配置修正后生效更快。第二,信任对象不同:iOS 绑定的是 TeamID.BundleID,Android 绑定的是包名加签名指纹,因此 Android 换签名(如启用 Play App Signing)必须同步更新指纹。第三,路径匹配规则不同:iOS 的 paths 支持通配与排除,Android 主要靠 intent-filter 的 host/pathPrefix 声明,二者需保持一致,否则会出现“iOS 能跳、Android 不能跳”。第四,回退行为不同:iOS 未命中直接进浏览器,Android 未验证时可能弹出应用选择器,影响体验一致性。

排查顺序建议:先确认关联文件可被公网 HTTPS 无重定向访问且 Content-Type 正确;再核对 App 侧声明(iOS 的 Associated Domains 能力、Android 的 autoVerify 与 intent-filter);最后检查签名/TeamID 是否与线上包一致。多数“不跳转”问题都落在关联文件不可达或签名指纹不匹配这两点上。