URL Scheme 是一种自定义 URI 协议,移动应用程序在操作系统中注册它以便通过 myapp://path 形式的链接打开。根据 RFC 3986,URI 方案定义了地址所有后续组件的语法和语义。当用户点击此类链接时,系统通过唯一标识符识别已注册的应用程序,并使用从链接中提取的参数启动它。基于 URL Scheme 的 深度链接 (Deep link) 仍然是移动平台上应用程序间导航的基本机制,尽管出现了更现代的替代方案。
要点
URL Scheme — 是应用程序在操作系统中注册用于通过自定义链接接收调用的唯一协议标识符。当用户点击 myapp://profile/123 之类的链接时,系统会识别已注册 myapp 方案的应用程序,并将控制权连同完整 URI 一起移交给它。这种机制允许应用程序在无需服务器基础设施的情况下交换数据并相互打开。
URL Scheme 的概念直接来源于 RFC 3986 网络标准,其中 URI 方案是任何通用资源标识符的第一个组成部分。在移动开发中,这一理念被应用于应用程序间通信,其中链接处理应用程序本身充当 HTTP 服务器的角色。
许多流行应用程序注册自己的 URL Scheme 以便与第三方服务集成。例如,Spotify 使用 spotify:// 方案,Telegram 使用 tg://,Instagram 使用 instagram://。开发人员也经常创建 appname:// 形式的方案用于内部导航和屏幕测试。
URL Scheme 仍然广泛用于 推送通知、电子邮件营销活动和二维码中,这些场景需要立即跳转到应用程序的特定部分。然而,从 iOS 9 和 Android 6 开始,出现了替代机制,逐渐补充和取代裸方案。
自定义 URI 的结构遵循通用规范 RFC 3986,由多个组件组成。方案首先指定,并通过冒号与地址的其余部分分隔。方案之后可以跟主机、端口、路径、查询参数和片段,每个都是可选的。
完整语法为 scheme://host/path?key=value#fragment。方案是唯一必需的要素,其余部分由具体实现的需求决定。方案后的双斜杠历史上源自 HTTP,并非规范严格要求的,但作为约定被普遍使用。
为了直观展示 URI 结构,使用组件表格。每个元素都有其用途和必需性级别。
| 组件 | 示例 | 必需性 |
|---|---|---|
| Scheme | myapp | 是 |
| Host | profile | 否 |
| Path | /user/42 | 否 |
| Query | ?id=42&tab=main | 否 |
| Fragment | #section2 | 否 |
开发人员可以自由选择 URI 结构,这提供了灵活性,但会在应用程序的不同版本之间产生 兼容性问题。建议将 URL Scheme 格式作为应用程序公共 API 的一部分进行文档化,并在变更时进行版本控制。
iOS 要求在项目的 Info.plist 文件中显式注册每个 URL Scheme。开发人员添加 CFBundleURLTypes 数组,其每个元素包含一个标识符 (CFBundleURLName) 和支持的方案列表 (CFBundleURLSchemes)。注册后,系统自动将所有对已注册方案的传入调用定向到应用程序。
传入 URL Scheme 的处理在应用程序委托中通过 application(_:open:options:) 方法进行。该方法接收一个 URL 对象,从中提取路径和查询参数以做出导航决策。处理必须返回一个指示操作成功的 Bool 值。
以下是用 Swift 语言实现的 URL Scheme 处理程序示例。代码演示了使用 URLComponents 从传入 URI 中提取主机和查询参数。
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any]
) -> Bool {
let host = url.host
let params = URLComponents(
url: url,
resolvingAgainstBaseURL: false
)?.queryItems
if host == "profile" {
navigateToProfile(params)
}
return true
}
该方法使用 URLComponents 安全地解析查询参数。这种方法优于手动解析字符串,因为它自动处理百分比编码和参数值中特殊字符的解码。
Android 使用 Intent Filter 系统基于 URL Scheme 路由深度链接。开发人员在 AndroidManifest.xml 中需要处理链接的 Activity 标签内声明过滤器。过滤器包含 action VIEW、BROWSABLE 和 DEFAULT 类别,以及指定方案、主机和 pathPrefix 的 data 标签。
当用户点击带有自定义方案的链接时,系统会检查所有已安装应用程序的 Intent Filter。如果找到多个匹配的应用程序,用户将看到选择对话框。BROWSABLE 类别允许从浏览器处理链接。
在 AndroidManifest.xml 中声明 Intent Filter 以处理 Activity 上 myapp 方案的示例。action 和 category 的组合对于深度链接的正确路由是必需的。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category
android:name="android.intent.category.DEFAULT" />
<category
android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="profile"
android:pathPrefix="/user" />
</intent-filter>
</activity>
配置过滤器后,需要在 Activity 中调用 intent.getData() 以获取 URI。检查 intent 和数据是否为 null 很重要,因为 Activity 可能在无传入深度链接的情况下启动,例如从启动器标准启动时。
URL Scheme 中的查询参数在问号之后以键=值格式传递,用 & 符号分隔。此格式与 HTTP 请求相同,易于被平台的标准工具处理。参数必须使用百分比编码对所有不属于允许 URI 字符集的字符进行编码。
带参数的完整链接示例:myapp://profile?userId=42&source=email&ref=abc123。提取 URL 后,应用程序顺序解析所有查询项,并根据其值做出导航到目标屏幕的决策。
在传递复杂数据时,务必考虑 URI 长度限制。在 iOS 中,URL Scheme 的最大长度限制为 2 KB,超过后系统将截断链接。在 Android 中,限制约为 8 KB,但具体数值取决于操作系统版本和设备制造商。对于大量数据,建议仅通过 URL Scheme 传递会话标识符,其余数据从服务器加载。
URL Scheme 的主要缺点 — 如果应用程序未安装在设备上,则无法处理链接。浏览器显示错误,用户丢失跳转上下文。为解决此问题,Apple 在 iOS 9 中引入了 Universal Links,Google 在 Android 6 中引入了 App Links。两种机制都通过与应用程序关联的网络域注册。
Universal Links 和 App Links 像普通 HTTPS 链接一样工作,但若应用程序已安装,则无需选择对话框即可打开。如果应用程序未安装,链接将在同一域上打开网页,保留用户体验。这使它们成为生产环境中的首选替代方案。
iOS 和 Android 上的 URL Scheme 没有内置的回退机制。开发人员使用中间服务器解决方案:链接指向一个网页,该网页通过 JavaScript 检查应用程序的安装状态,并重定向到方案或应用商店。Firebase Dynamic Links 和 Branch.io 提供此问题的现成解决方案,支持延迟深度链接 (deferred deep link),自动确定安装状态并引导用户,无需开发自定义服务器管道。
在 iOS 15+ 和 Android 12+ 中使用 URL Scheme 时会出现额外的复杂性,这些版本的隐私规则更加严格。Safari 会阻止未经事先确认打开未注册方案的尝试,而 Android 12 通过 PackageManager 限制已安装应用程序的可见性。这些变化使得使用 URL Scheme 进行应用程序间交互的可靠性低于早期版本的平台。
常见问题
URL Scheme 使用无加密的自定义协议,而 Universal Links 通过 HTTPS 工作并带有域验证。Universal Links 不会弹出应用程序选择对话框,并且在设备上无应用程序时也能正确处理。
可以,但所有非 ASCII 字符都必须根据 RFC 3986 通过 百分比编码 (percent-encoding) 进行编码。建议避免在 URL Scheme 中使用西里尔字符,以确保与旧版本操作系统和浏览器的兼容性。
iOS 和 Android 对方案数量均无限制。实践中,应用程序使用一到五个方案。例如,Telegram 注册了 tg://、t.me/、telegram:// 和 telegram.me:// 方案。
在 iOS 中使用 canOpenURL(_:) 方法,如果存在已注册的方案则返回 true。在 Android 中通过 PackageManager.queryIntentActivities() 进行检查。两个平台都要求在配置中预先指定方案。
不可以,URL Scheme 不对数据进行加密。任何注册了相同方案的应用程序都可以拦截链接。为了安全,请使用带 HTTPS 的 Universal Links 或在协议层面进行数据加密。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。