Appy
← 返回博客
指南

不用 SDK 的延迟深度链接:底层到底是怎么运作的

不嵌入归因 SDK,也能把链接意图带过应用安装这道坎。本文讲清 Android 和 iOS 上什么能真正穿过商店边界、什么会断掉,以及每种变通方案的隐私代价。

2026年8月14日•阅读约 11 分钟

上下文死在商店边界

有人点了指向 /product/42 的链接。应用未安装,你的重定向把他送进商店。他安装、打开应用,落在一个通用首页上。承载他意图的那个 HTTP 请求终结在商店页面:刚装好的应用启动时没有 URL、没有 cookie、没有 referrer。点击留下的东西一样都没活下来。

把这份上下文带过安装,就是延迟深度链接(deferred deep linking),标准答案是"嵌一个 MMP SDK"。很多团队不想要这个答案——Apple 开发者论坛的 772811 号帖子只是众多"不用第三方 SDK 怎么做"提问中的一个——而 2026 年 8 月 AppsFlyer 把延迟深度链接移出免费套餐后,问的人更多了。那么自己能不能做?Android 上可以,干净且官方。iOS 上勉强可以,靠的是有实际代价的变通方案。

先说明范围:延迟深度链接是什么、什么时候值得做,我们在另一篇文章里讲。本文只谈管道工程:哪种运输方式能把一个令牌偷渡过安装,以及每一种能信到什么程度。

什么能活过安装,什么不能

携带 install referrer普通商店链接上下文丢失点击点击商店商店安装安装首次打开首次打开
上方泳道:令牌搭乘 Play 的 referrer 参数穿过商店,在首次打开时从另一侧出来。下方泳道:普通商店链接,商店页面一加载,点击上下文就没了。

Android:Install Referrer API 是实打实的好消息

Google Play 原生解决了这个问题。Play 商店 URL 接受一个 referrer 查询参数,Play 把这个字符串随安装一起保存,应用在首次启动后可以通过 Install Referrer API 原样读回。机制就这么多:没有指纹、没有剪贴板、不用猜。

严格说不算"零代码":读取该值需要 com.android.installreferrer 库——一个只在本地与设备上的 Play 商店应用通信的小构件。它不向任何人发起网络请求,不是往外打电话的分析 SDK,除了你自己怎么处理这个字符串,不给你的隐私声明增加任何负担。

参数里放什么由你决定:链接 ID、活动名、编码后的深度链接目标。保持简短并做 URL 编码——play.google.com/store/apps/details?id=com.example&referrer=tok_8f2 中的 referrer 会原样返回。

端到端的完整闭环

  1. 1链接被点击时,重定向在 Play URL 上追加 referrer=<token>,令牌指向存在你服务器上的点击上下文。
  2. 2首次启动时,应用连接 InstallReferrerClient 并读取 installReferrer 字符串。
  3. 3应用用令牌到你的 API 换回保存的上下文——深度链接路径、活动信息——并路由用户。
  4. 4服务器把令牌标记为已消费。referrer 字符串会留在设备上,所以一次性使用必须在服务端强制执行。

限制窄而可预期:该 API 只覆盖真正经由 Google Play 完成的安装。侧载、华为应用市场和其他商店不携带 Play referrer;该值应在首次启动后不久读取一次,而不是当作永久存储。

Appy 链接已经替你做好了 Android 这一半

自 2026 年 9 月起,每条把用户送到 Google Play 商品页的 Appy 链接都会自动加上 referrer 参数。不需要任何配置,也不用加 SDK。referrer 里始终带有 appy_slug,也就是被点击链接的 slug,应用据此就能知道是哪条链接带来了这次安装。这部分在所有套餐上都可用,包括 Free。

如果链接开启了参数传递,点击时的 query string 也会一起放进 referrer。一个指向 appy.to/summer?screen=product-42&utm_source=poster 的二维码,在首次启动时会以下面这串字符到达应用。整个过程我们这边不存任何东西,也不靠猜测做匹配。

被扫描或点击的链接
https://appy.to/summer?screen=product-42&utm_source=poster
发往 Google Play 的地址
https://play.google.com/store/apps/details?id=com.example.app&referrer=appy_slug%3Dsummer%26screen%3Dproduct-42%26utm_source%3Dposter&screen=product-42&utm_source=poster
应用首次启动时读到的内容
appy_slug=summer&screen=product-42&utm_source=poster

后台里的设置

参数传递是 Business 套餐里按链接单独开关的设置。创建链接时它是“启用参数传递”开关;在已有链接上,同一个设置叫“传递传入参数”。关闭时,referrer 仍然会带上 appy_slug,但不带别的。

有几条规则值得知道。你自己写进 Play URL 的内容优先:如果目标地址里已经有 referrer=utm_source%3Dposter,Appy 会保留这个值,再在旁边加上 appy_slug。appy_slug 本身不能通过 query string 覆盖,所以一条链接没法冒领别的链接带来的安装。另外,如果传递的参数会让 referrer 超过 1 KB,Appy 会丢掉它们,只保留 slug。

参数是明文传输的。活动名称或页面 ID 没问题;如果涉及个人信息,或者值比较多,请只传一个短 token,再在你的服务器上解析,做法见下文的设计模式。

在应用里读取

引入 Install Referrer 库,在首次启动时读取一次,然后像解析 query string 一样解析它。客户端要做的就这些:

implementation("com.android.installreferrer:installreferrer:2.2")
fun readAppyReferrer(context: Context, onResult: (Map<String, String>) -> Unit) {
    val client = InstallReferrerClient.newBuilder(context).build()
    client.startConnection(object : InstallReferrerStateListener {
        override fun onInstallReferrerSetupFinished(responseCode: Int) {
            val raw = if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
                runCatching { client.installReferrer.installReferrer }.getOrNull()
            } else null
            client.endConnection()
            val uri = Uri.parse("?" + raw.orEmpty())
            onResult(uri.queryParameterNames.associateWith { uri.getQueryParameter(it).orEmpty() })
        }

        override fun onInstallReferrerServiceDisconnected() = Unit
    })
}

readAppyReferrer(this) { params ->
    val slug = params["appy_slug"] ?: return@readAppyReferrer
    params["screen"]?.let { openScreen(it) }
    analytics.logInstallSource(slug, params["utm_source"])
}

只在安装后的首次启动时运行,并记下已经运行过。referrer 会一直留在设备上,如果每次冷启动都读,用户就会反复被带到同一个页面。从 AppGallery 或直接用 APK 安装的用户不会带 referrer,这时直接打开正常的首页就好。

iOS:没有官方等价物,三条变通路径按序排列

Apple 没有提供任何类似 Install Referrer 的东西。App Store 商品页不会把任意参数带过安装;下面的一切都是建立在这个空缺上的变通方案。从最不坏到最小众:

剪贴板传递

01 · 中等可靠性,受权限限制

链接落地页把一个短令牌写入剪贴板——放在按钮点击后面,因为 Safari 要求用户手势才能编程复制——然后跳转 App Store。首次打开时,应用读取剪贴板,找到令牌并向你的服务器兑换。

麻烦从 iOS 16 开始:读取剪贴板会触发系统粘贴弹窗。用户在三十秒前才装好的应用的第一个屏幕上看到"允许从 Safari 粘贴吗?",很多人会拒绝。拒绝是无声的损失——你无法把它和"本来就没有令牌"区分开。UIPasteboard.detectPatterns 可以不弹窗地检查模式,但读不到值。而且从点击到首次打开之间,剪贴板还可能被覆盖。它确实常常有效,但硬币的另一面清晰可见。

概率匹配,诚实的做法

02 · 带分数的推测,永远不等于确定

点击时,服务器保存一条简短记录:哪条链接、哪个平台、user agent 里的系统版本、时间戳,以及一种识别点击来源网络的方式。首次打开时,应用会去找一条近期点击,看它像不像同一网络里的同一部手机。全程没有任何令牌传递。匹配本身就是推测,真正的问题只在于它肯不肯承认。

做得草率,它就配得上坏名声:原始 IP 地址被保存好几天,最佳候选被当作确定结果返回,同一个办公室 Wi-Fi 里的陌生人还能领走别人的邀请奖励。信号本身也比看起来弱。iCloud Private Relay 会隐藏地址,运营商级 NAT 让许多手机共用一个地址,Android 上的 Chrome 把每部手机都报成 Android 10,iOS 26 上的 Safari 则把版本冻结在 18.6。

做得认真,它仍然是推测,但是有用的推测。给每个候选打分,把分数连同得出它的信号一起返回;永远不要让推测等同于确定。让应用自己选阈值,低于阈值的点击不要消耗掉,这样真正点过链接的那部手机还能认领。窗口要短,也不要存储 IP 地址。Appy 的 Enterprise SDK 把这些工作都留在 Appy 这一侧:应用拿到的要么是链接,要么什么都没有;你的后端可以查询某次安装是否经过核实;识别网络靠的是地址的带密钥哈希,每天轮换,两小时后过期。开启严格归因后,只统计经过核实的安装,其余都算自然安装。

App Clip 及其他小众路径

03 · 可靠但狭窄

从你的链接唤起的 App Clip 能拿到完整 URL,并在安装后通过共享容器把上下文交给完整应用。在它的适用范围内,这是 iOS 上最可靠的路径——但需要构建和维护一个 App Clip,且只适合轻量即时体验说得通的流程。Shared web credentials 解决的是登录连续性这个相邻问题,不是一般的链接上下文。值得了解,很少是答案。

与机制无关、始终成立的设计模式

无论令牌由什么运输——referrer 字符串、剪贴板,甚至概率匹配 ID——服务端架构都是同一套,而把它做对比选哪种运输更重要。不要把整个深度链接塞进通道:把点击上下文存在服务器上,只移动一把短命的钥匙。

01

用随机令牌为上下文建档

点击时以短随机令牌为键写入 {deep_link, campaign, timestamp},通道里只放令牌。负载保持小巧,活动数据不会散落在剪贴板和 referrer 日志里,而且"上下文"的含义可以随时调整而不动客户端。

02

激进地过期

30 到 60 分钟的 TTL 足以覆盖真实的点击到安装旅程。点击一周后才解析的延迟路由,错的次数比对的多。

03

强制一次性使用

首次兑换即消费令牌。referrer 字符串留在设备上,剪贴板可以被读两次;没有一次性约束,重装或第二台设备就能重放别人的上下文。

04

失败时回落到默认,而不是去猜

没有令牌、令牌过期、粘贴弹窗被拒——让用户落在正常首页。通用的首次打开只是小小的遗憾;被路由进别人的购物车是一张工单。

可靠性,说实话

来自在生产环境跑这些机制的团队的粗略数字。你的流量来源组合、安装延迟和受众的 iOS 版本都会让每个数字移动。

机制平台可靠性备注
Play Install ReferrerAndroid高官方 API,延迟安装也能存活,无用户弹窗。仅限 Play 安装。
剪贴板令牌iOS中受 iOS 16+ 粘贴弹窗限制;拒绝是无声的;剪贴板易失。
带分数的概率匹配iOS,以及无 referrer 的 Android中,带分数同一网络、系统版本与时间。点击后几分钟内在同一 Wi-Fi 下很可靠;跨网络、CGNAT 和 Private Relay 下较弱。只有配合可见的分数和阈值才值得使用。
App Clip 交接iOS高但狭窄确定性强,但只适合值得为其构建 App Clip 的流程。

这对你的技术栈意味着什么

如果你的延迟路由需求只在 Android,你需要的东西很少:一条会追加 referrer 参数的链接、那个小库、一个兑换令牌的端点。一个下午的工作量,而且在真正要紧的意义上无 SDK——没有第三方代码在观察你的用户。

iOS 才是托管方案挣饭吃的地方,因为在那里变通方案本身就是产品:匹配基础设施、一个得有人盯着、让它保持诚实的评分模型,以及每一个改变地形的系统版本,比如 iOS 26 冻结了 Safari 的版本号。这也是 Appy 把带延迟深度链接和安装归因的 iOS 与 Android SDK 放进 Enterprise 套餐的原因;Android 的 referrer 则每条链接都自带。

不过要诚实面对自己的问题是哪一个。如果你真正需要的是"已装应用的用户落到正确页面",那是标准深度链接——不存在安装边界。Appy 的深度链接垫片在 Pro 套餐(每月 9.99 美元)覆盖这种场景,没装 App 的用户会被送去商店,同样无 SDK。在购买更重的机器之前,先量一量漏斗里有多少是真正带上下文的新装。

延伸阅读

常见问题

扫二维码也能用 Install Referrer 吗?

能,前提是二维码经由一条会把 referrer 参数追加到 Play URL 的链接解析。机制不关心点击怎么开始——扫码、点击还是 NFC——只关心 Play 打开时商店 URL 是否带着 referrer。直接指向裸 Play URL 的二维码什么都带不了。

我的剪贴板令牌方案为什么突然坏了?

几乎可以肯定是 iOS 粘贴弹窗。从 iOS 16 起,应用第一次读剪贴板会触发系统权限对话框,相当比例的用户会拒绝。你的代码没有退化;你的读取环节里现在多了一个人。看看令牌命中率是下降而不是归零——那就是它的签名。

iOS 上允许概率匹配吗?

Apple 的开发者协议禁止为唯一识别设备而从设备提取数据,集成了这类 SDK 的应用也可能被 App Review 拒绝。能在不同应用之间、跨越数周认出同一部手机的指纹,显然越界了。针对你自己的链接,在两小时内把同一网络中的一次点击和一次首次打开做比较,不存 IP,并附上分数,这个范围要窄得多,但你仍然需要在隐私信息里说明这些数据。无论用什么工具,都要问清它存什么、存多久,以及它在猜的时候会不会告诉你。

我到底需不需要延迟深度链接?

只有当安装后的落地页面对漏斗有实质影响时才需要。如果大多数安装来自没有具体目的地的通用投放,一个好的引导流程比延迟路由更值钱。先测量:数一数存在真实深度链接目标且应用未安装的点击。数字小,就把精力花在别处。

继续探索

产品
2026年10月1日
8 分钟阅读

看清每次安装来自哪条链接:Appy 的 iOS 与 Android SDK

Appy 的链接现在能跟到应用商店之后。新的 SDK 为每条链接、每个二维码、每个营销活动统计安装、应用内事件和收入,还让新用户第一次打开就进入对的页面。

install attribution
deferred deep linking
install referrer
mobile attribution
阅读文章
指南
2026年10月1日
7 分钟阅读

Chrome 让每部手机都自称 Android 10

Android 上的 Chrome 把每部手机都报成 Android 10、型号 K。这给延迟深度链接的匹配带来了什么问题,为什么 Critical-CH 会把每次点击算两遍,以及 Client Hints 如何找回真实的版本和型号。

android
user agent
client hints
deferred deep linking
阅读文章
指南
2026年5月14日
9 分钟阅读

延迟深度链接(deferred deep linking):原理与使用时机

把原始链接意图带过安装环节,让新用户首次打开应用就直接进入对应页面,而不是默认首页。

deferred deep linking
install referrer
deep linking
mobile attribution
阅读文章

想看更多内容?浏览 博客全部主题.

把你已经赚到的安装路由到正确的地方

每条链接都自带 Play install referrer,Pro 套餐的深度链接还会把没装 App 的用户送去商店,都不用往应用里放 SDK。需要在 iOS 和 Android 上做延迟深度链接和安装归因时,Enterprise SDK 会把每次安装归到带来它的链接上。

开始使用 Appy