Android App Links 验证失败:用 adb 五分钟定位问题
从 Android 12 起,域名验证未通过的 App Link 会在浏览器而非你的应用中打开——静默发生,而且并非每台设备都一样。这份 adb 排查清单能快速找到原因,包括大多数事故背后的 Play 签名密钥不匹配问题。
应用装着,链接却打开了 Chrome
它通常以工单而非报错的形式出现。用户点了指向你域名的链接,应用就在手机上,打开的却是 Chrome。按 Android 文档的说法,从 Android 12 起,https 链接只有在系统于安装时验证过你的域名后才会打开应用——而验证失败时是静默失败。没有崩溃,没有提示,仪表盘里什么也看不到。
有两个特性让排查格外痛苦。验证按域名进行:example.com 可能已验证,promo.example.com 却悄悄没有。它还按设备进行:同一个构建在 Pixel 上通过,在三星手机上却失败——三星开发者论坛的帖子里反复出现这种组合。如果你正处在事故当中,直接跳到下面的诊断部分。
两个域名,两种裁决
安装时到底发生了什么
应用安装或更新时,Android 会读取每个用 autoVerify 声明的域名,并逐一抓取 https://你的域名/.well-known/assetlinks.json。按 Android 文档,该文件必须以 HTTPS 直接返回 200,content type 为 application/json——验证器不跟随重定向。结果按域名缓存,这就是为什么一个子域名已验证、它的兄弟却没有。
文件里的 sha256_cert_fingerprints 必须匹配设备上 APK 的签名证书。头号陷阱来了:启用 Play App Signing 后,那个证书是 Google 的应用签名密钥——不是你的上传密钥。本地 keystore 的指纹能完美验证手动安装的构建,却会让每一次来自 Play 商店的安装失败。「在我设备上是好的」的报告里,一半都追溯到这里。
开始前还要知道一点:裁决会被缓存。修好服务器上的 JSON,对已经失败的手机毫无作用,直到验证再次运行——幸好这可以强制触发。
五分钟诊断
接上任意一台装有应用的 Android 12+ 设备,按顺序执行。多数事故在第三步就能解决。
读取验证状态
adb shell pm get-app-links com.example.app这条命令打印应用声明的每个域名及其状态。verified 表示握手通过;none 表示验证器从未批准该域名——把用户送去 Chrome 的正是它。多用户设备上,也要检查输出底部按用户划分的部分。
像验证器那样抓取 assetlinks.json
curl -sI https://example.com/.well-known/assetlinks.json你要看到直接的 200。任何 301 或 302 都会让验证失败——按 Android 文档,验证器不跟随重定向。确认 content-type 是 application/json,然后抓取正文,亲眼核对包名和指纹。/.well-known/ 前面的反爬或登录墙同样算失败。
对照 Play 签名密钥核对指纹
Play Console → App integrity → App signing key certificate打开 Play Console 的这个页面,把上面的 SHA-256 和你的 JSON 放在一起。必须逐字节一致。如果文件里是上传密钥的指纹,问题就找到了——加入 Play 签名密钥指纹;如果还在 Play 商店之外分发构建,两个都保留。
强制重新验证
adb shell pm verify-app-links --re-verify com.example.app修好服务器端后,让设备再试一次。验证器是异步运行的:等几秒,重复第一步,观察 none 变成 verified。部分三星设备的状态要重启后才会变——三星开发者论坛报告过这一模式。
像真实点击一样触发 intent
adb shell am start -a android.intent.action.VIEW -d "https://example.com/promo"这是端到端测试。已验证的域名会直接打开应用,没有选择器。如果出现浏览器或选择弹窗,说明状态还没翻转——先复查第一步,再下结论说修复失败。
如果不想在事故中肉眼核对 JSON,我们的 链接校验工具 会抓取你的 assetlinks.json,一次性检查状态码、content type 和指纹格式。
常见元凶
五个原因覆盖了我们见过的几乎所有报告。左边原因,右边修法。
JSON 里是上传密钥指纹,而不是 Play 签名密钥
从 Play Console → App integrity → App signing key certificate 复制 SHA-256。如果还分发上传密钥签名的构建,两个指纹都保留。
assetlinks.json 返回重定向或 404
在每个声明的主机上直接以 /.well-known/assetlinks.json 提供文件。对验证器来说,www 和裸域是两个不同的域名。
错误的 content type,如 text/html 或 text/plain
配置服务器或 CDN 对该路径返回 application/json。正文再正确,头错了照样失败。
多个 flavor 或包名,文件里缺了一个
这个文件是数组:每个包名一条声明,各带自己的指纹。缺了哪个 flavor 就只有那个失败,在 QA 里看起来像随机故障。
修好 JSON 后三星设备仍停在 none
部分三星机型的延迟验证在三星开发者论坛是常客。先强制重新验证,再重启设备,然后才下别的结论。
Android 15 和 16 改了什么
最近两个版本把标准朝同一个方向推进:受支持的路径是经过验证的 https App Links,其余的路都在收窄。
按平台发行说明,加入了 Dynamic App Links:域名关联可以在不发布新 APK 的情况下更新,缩短了从修好 JSON 到设备生效之间的周期。
按平台发行说明,收紧了自定义 URI scheme 的验证方式。如果你的流程还依赖 myapp:// 这类 scheme,把它们当作遗留方案,把入口迁到已验证的 https 链接上。
智能链接的诚实定位
智能链接在服务器上完成路由,先于上述所有机制:有人打开 Appy 链接时,重定向读取设备信息,把 Android、iOS 和桌面访客送往不同目的地。这套路由在所有套餐可用,也不依赖设备端验证——因为它发生在操作系统看到你的域名之前。
也要说清它不能做什么:智能链接无法绕过 App Links 验证。重定向最终落在你的 https 域名上,打开应用还是浏览器,仍由那个 verified 或 none 的状态决定。它在事故中给你的是一扇可控的前门——修 assetlinks.json 期间,把 Android 流量指向 Play 商店页或网页回退,之后再把目的地改回来,已印刷的东西一个都不用动,因为动态二维码在印刷后仍可编辑。分析数据还会告诉你这段时间有多少 Android 流量撞上了坏掉的路径。
无论如何都要把验证修好。Pro 上的深度链接目的地、Enterprise 上跨越安装间隙的延迟深度链接都值得拥有——但它们建立在一份正常工作的 assetlinks.json 之上,而不是替代它。
常见问题
为什么同一条链接在一台设备上正常,在另一台上不行?
验证在每台设备的安装时各自运行,所以每部手机都带着自己缓存的裁决。安装时的网络波动、行为不同的 OEM 验证器——三星是其开发者论坛上被报告最多的案例——或早于你修复 JSON 的安装,都会让同一构建呈现不同状态。
需要发应用更新才能修复吗?
通常不需要。如果 manifest 已用 autoVerify 声明了域名,故障就在 JSON 文件或指纹里,都在服务器端。修好文件、强制重新验证,已安装的构建就会开始打开链接。只有域名从未在 manifest 里声明时才需要更新。
智能链接能绕过 App Links 验证吗?
不能——对任何相反的说法都要警惕。重定向在服务器端选择目的地,但访客一落到你的 https 域名,操作系统就照常应用同样的验证状态。智能链接改变的是验证坏掉期间你能做什么——导向商店或网页——而不是它坏没坏。
验证多久会自己重试?
按 Android 文档,验证器会重试失败的域名,并在应用更新时重新运行,但时间表没有保证,各 OEM 也不同。实际上是数小时到数天。用 adb 强制重新验证,或重装应用,都比干等快。
继续探索
AppsFlyer 免费套餐刚刚缩水:哪些功能转为付费,你有哪些选择
延迟深度链接、品牌域名、Smart Banner、批量链接和 API 访问已移出免费 Zero 套餐。本文讲清改了什么、替代要花多少钱,以及一个下午就能完成的迁移路径。
面向深度链接、Universal Link、应用商店和网页的参数传递
了解如何在 App 打开、应用商店跳转、Universal Link 和网页回退之间保留活动与上下文参数,同时保护目标中的固定值。
延迟深度链接(deferred deep linking):原理与使用时机
把原始链接意图带过安装环节,让新用户首次打开应用就直接进入对应页面,而不是默认首页。
想看更多内容?浏览 博客全部主题.