KappS 知识库 · 博客

Android App Links 2026 实操教程:清单、指纹与验证排错

博客 教程

Android App Link 是 Google 唯一当作「所有权证明」来对待的深度链接机制:应用声明自己愿意处理的 HTTPS 主机,在那个主机上发布一份 Digital Asset Links 语句清单,Android 核对两边一致之后,才会把链接交给你的应用、而不是浏览器。决定成败的只有两样东西——你自己控制的 /.well-known/assetlinks.json,以及写进文件里的 SHA-256 签名指纹。绝大多数失败不是玄学,而是这两者之一出了偏差。

下面按实际动手顺序展开:声明带 autoVerify 的 intent-filter、发布 assetlinks.json 语句清单、在 Play App Signing 重新签名的情况下选对指纹、安装后用 adb 读真实验证状态、排查线上最常见的那几类失败、设计一个不骗用户的兜底路径,以及让验证在换密钥和换域名时依然成立。贯穿全篇的一条原则:验证的本质是「这个域名和这个应用属于同一个主人」,所以唯一正当的通过方式,就是这两者真的都属于你。


一、验证到底证明了什么,又不证明什么

有三个概念经常被混在一起,把它们分开,很多设计错误就不会发生。

  • 深度链接(deep link)指任何能打开应用内某个页面的 intent。它可以是自定义 scheme,例如 myapp://product/42,但设备上任何应用都可以注册同一个 scheme,于是恶意应用可以抢走这条 URL。
  • App Link 则是一个 HTTPS 地址,例如 https://shop.example.com/product/42,Android 已经验证过你有权处理它——因为该主机发布了一份语句,写明了你的包名和签名证书。用户看到的是一条正常的网页地址,浏览器随时能打开,而验证只允许一个应用胜出。
  • 网页兜底指应用没安装时,同一个地址在浏览器里打开。一套合格的链接策略要让这三者互相吻合:无论应用在不在,地址都能落到想要的内容上。

反过来,验证不做什么:它不能证明应用已安装,不给你对该域名内容的任何权利,也不能替代身份认证。页面上要展示账号数据,真正把关的仍然是服务器端的 token 校验。验证只回答一个问题——「这个应用有没有资格打开这个主机的链接」。

实践结论很直接:自定义 scheme 最多只用于应用内部的跳转,重置密码、支付回跳这类场景绝对不要依赖它,因为 scheme 的归属根本无法强制。

二、第一步:在 manifest 里声明 autoVerify 过滤器

从 AndroidManifest.xml 开始。过滤器必须是 https,必须带 android:autoVerify="true",并且同时包含 VIEW action 与 DEFAULT、BROWSABLE 两个 category:

<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="shop.example.com" />
</intent-filter>

下面这些细节决定它到底能不能用:

  • 一个主机一个过滤器。验证是按主机逐个评估的,官方也建议每个域名单独写一个 <intent-filter>。把五个主机挤进同一个过滤器,等于让其中一个出问题就拖垮全部。
  • 主机名是字面量。android:host 不支持通配符。子域名要单独声明,同时用 android:path、android:pathPrefix 或 android:pathPattern 收窄范围,别让应用去抢它渲染不了的地址。
  • http 不参与验证。自动验证只认 https。留着 http 过滤器只会制造重复的选择器弹窗。
  • 只声明你真的会路由的路径。过滤器匹配整个主机、应用却只处理三条路径,其余地址会掉进空白页——这种体验会先出现在商店评论里,而不是崩溃后台里。

如果应用同时要处理营销域名和业务域名,先明确哪个是「对外分享用的规范域名」。每增加一个主机,就多一份要发布的文件和一份要保持同步的证书。

三、第二步:发布 /.well-known/assetlinks.json

语句清单只有一个正确位置,必须走 HTTPS,而且前面不能有任何跳转:

https://shop.example.com/.well-known/assetlinks.json

正文是一个 JSON 数组,每个元素是一条语句。普通应用处理该主机的全部地址时,一条就够:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.shop",
    "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"
    ]
  }
}]

多个包、或一个包多张证书,往同一个数组里追加语句即可。托管细节和内容本身一样重要:

  • 内容类型。以 application/json 返回。个别系统版本和部分 CDN 边缘节点对此很严格。
  • 禁止跳转。文件前挂一个 301 或 302,抓取就会失败,而且在设备上完全静默。这是最常见的托管错误——全站「强制 HTTPS」或「去掉末尾斜杠」的规则会悄悄把它弄坏。
  • 不要认证、不要人机校验。Google 的验证器是匿名抓取。WAF 挑战页或地区封锁会让它拿到一个 HTML 错误页,而它期待的是 JSON。
  • JSON 必须合法。多一个尾逗号、或塞一行 // 注释,整个文件连同里面所有语句一起失效。

动手排查设备之前,先按验证器的方式取一次文件:curl -sI https://shop.example.com/.well-known/assetlinks.json 应该返回 200 且没有 Location 头,正文能被解析。Google 提供了语句清单生成器与校验工具,结构和指纹格式都可以先在那里过一遍再上线。

四、第三步:用设备真正看到的那个指纹

验证失败大多发生在这里,原因就是 Play App Signing。一旦启用,应用签名密钥保存在 Google 一侧,Play 分发的每个包都由它重新签名。也就是说,用户设备上安装的证书是应用签名证书,而不是你构建机上的上传密钥。

  • 从 Play 安装的版本需要 app signing key 的 SHA-256 证书,位置在 Play 管理中心 应用完整性 → 应用签名(App signing key certificate 那一栏)。
  • 本地构建与侧载包用的是上传密钥或调试密钥,需要那一张证书的指纹——用 keytool -list -v -keystore <keystore> 读出 SHA-256 那一行。
  • 两个都写。把应用签名证书和上传证书放进同一个 sha256_cert_fingerprints 数组是正常做法:Play 安装的版本能验证通过,QA 的本地包也不会失效。

同一区域还有两个坑。调试密钥的指纹只属于生成它的那台机器,自己测可以,写进线上文件没有意义。另外,一旦做过 Play 的密钥升级,证书就变了:语句清单必须在灰度放量之前更新,否则新安装的链接直接失效,而老安装看上去一切正常。

指纹要从控制台复制,不要手打。错一个十六进制字符,文件照样是合法 JSON、照样返回 200,验证照样失败,而且哪儿都不报错。

五、第四步:安装,然后读真实的验证状态

验证在安装时触发,应用更新后也会重跑,所以测试时必须用你真正要验证的那个渠道装包:从 Play 内部测试轨道装的包,和用 adb install 装上传签名的包,行为并不一样。

Android 12 及以上可以直接问系统:

adb shell pm get-app-links com.example.shop

输出是按域名分组的状态:域名、用户是否已批准、以及验证结果——语句清单对上了是 verified,没对上会是 denied 或 legacy_failure。更老的版本上,等价做法是导出域名验证记录:

adb shell dumpsys package d

如果某个主机显示 denied,而文件你刚刚才修好,那就强制重跑一次,别盲目重装:

adb shell pm set-app-links --package com.example.shop --domain shop.example.com all

然后按用户的方式实测。在浏览器地址栏输入的链接、在邮件客户端里点的链接、在另一个应用里点的链接,解析路径并不总是一致,可能只有一个会走选择器。同时检查系统设置里应用的默认打开页面:用户可以把已通过的链接关掉,你的代码要在这种情况下依然可用,而不是崩掉。

六、第五步:排查线上真正会遇到的那几类失败

实际故障几乎都能归进五个篮子,按这个顺序查,每一项只要几秒。

  1. 指纹写错。文件能解析、主机也对,但语句里写的是上传密钥,而设备上是 Play 签名的包。把控制台的指纹和文件里的逐字符比一遍。
  2. 跳转或内容类型不对。curl -sI 看到 3xx、text/html,或 CDN 缓存的错误页。先修响应头、清缓存,再重新验证。
  3. 包名或主机不匹配。白标构建改过 application ID,或过滤器里还留着一个已经不再提供文件的测试主机。把 manifest 里每个主机都 grep 出来,逐个确认有对应语句。
  4. 验证根本没跑。安装时断网,或装完之后从未启动过应用。联网打开一次应用,再跑 pm get-app-links。
  5. 用户手动关掉了。该域名在设置里被停用——可能几个月前,也可能是换机迁移工具干的。pm set-app-links … all 可以重置用于测试,但对真实用户只能引导,不能强改。

把一行冒烟检查放进发版清单:抓文件、解析、断言指纹等于控制台当前展示的那一个。域名搬迁或密钥升级跳过了这一步,就是一套本来正常的链接配置悄悄死掉的全过程。

七、第六步:设计一个说实话的兜底

App Links 让应用成为首选处理者,但不是唯一处理者。先设计网页这一侧,因为那是永远存在的场景。

  • 地址必须在没有应用时也能打开。只有装了应用才有效的分享链接就是坏链接,顺带也废掉了要索引它的搜索引擎。
  • 先识别,再询问,别硬跳。一条带「在应用中打开」和下载入口的横幅,比自动跳转更能转化,也不会把本来就想用浏览器的人困住。
  • 应用内的路径和网页路径保持一致。应用渲染 /product/42,同一路径的网页就应该是同一个商品;你今天不想维护的那张映射表,下个季度一定会坏。
  • 处理「装着装着才需要打开」的情况。延迟深度链接——新用户装完应用后直接落到分享的那件商品——需要服务端机制或 Install Referrer 之类的能力。过去大家普遍依赖的 Firebase Dynamic Links 已经停服,所以要按 Install Referrer API 或自建方案来规划,别假设有个现成托管服务永远在。

也别把验证做成「隐身劫持」。应用渲染不了的地址,交给浏览器打开就是更好的结果,既不给用户一个「页面不存在」,也不毁掉域名的搜索表现。

八、让验证在发版和换域名之后继续成立

验证不是一次性配置,而是三样东西之间的长期约定:manifest、已发布的文件、以及商店实际签名的证书。任何改动碰到其中一样,都要进发版清单。

  • 密钥升级或重新签名。把新证书更新进 assetlinks.json,在放量之前先发布;旧证书保留到旧版本彻底退场为止。
  • 更换域名。先在新主机发布语句,再发带 manifest 改动的版本。反过来做,等于上线瞬间把链接全部打死。
  • 品牌域名与地区域名。每个主机都是一份独立的语句和文件。把部署自动化,让新增地区域名不可能漏发。
  • 所有权边界。这份文件是「我控制该域名」的公开声明。只为自己组织拥有并运营的域名发布,绝不要替第三方托管别人的语句——那正是滥用检测重点盯的模式,会让域名和应用账号同时陷入风险。

把 manifest 过滤器、发布用 JSON 模板和当前指纹放在一处,由单一来源生成。一旦变成三处手工编辑,两个发版周期内必然漂移。

九、KappS 在这件事里做什么

我们做的是上架过程中运营侧的那部分工作:让账号、发版和商店记录保持经得起平台审查的状态——准备并提交合规材料,而不是去绕过检查。放在链接验证这个场景里,通常是这几件事:

  • 发版卫生。控制台、构建产物和已发布语句清单里的证书与 application ID 保持一致,确保设备看到的指纹就是你记录的那一个。
  • 账号准备度。开发者验证、付款资料和税务信息与账号背后的法律实体一致,避免上线被一次不相干的审核卡住。
  • 域名与托管复核。确认 well-known 路径确实返回 JSON,前面没有跳转、没有人机校验、没有缓存住的错误页。

我们不做的:为客户不拥有的域名发布语句、伪造指纹、或帮应用抢一个它无权声明的主机。验证机制存在的目的正是防这类行为,这不是我们提供的服务。

资料来源(官方文档):Android Developers —— Verify Android App Links、Add Android App Links、Digital Asset Links 规范;Play 管理中心帮助 —— 应用签名证书与密钥升级;Android Developers —— adb shell pm 应用链接命令。

正在规模化运营开发者账号?运维这一块交给我们。

点击选择文件 · 右键「粘贴」或按 Ctrl/⌘+V 直接粘贴截图

也可以直接拖拽文件到此处。

我们会回复你填写的邮箱,信息仅用于本次咨询。

✔ 已提交

我们已收到你的信息,会在 24 小时内回复。


← 返回修改