本专题为软件平台路由规则设计 (含移动端、Web、电脑、手表与手环等智能穿戴)。本文对应「Universal Links / App Links」阶段,介绍基于 HTTPS 的可验证深链接在 iOS 、Android 、HarmonyOS 、Flutter 、MacOS 、WebApp 、ReactNative 等平台上的配置、校验流程与 App 内路由对接,含多端代码示例与流程图。多端总览见 08-软件体系与多平台路由对照。
一、概念与定位
1.1 什么是 Universal Links / App Links
- Universal Links (iOS 9+):使用标准 HTTPS URL (如
https://example.com/app/detail/1)打开 App 内指定内容。通过域名下的 apple-app-site-association 文件证明「该域名由该 App 拥有」,系统在用户点击链接时可直接打开 App 而无需经过 Safari 或弹窗,未安装时则在浏览器打开同一 URL,实现「一条链接、两种体验」。 - App Links (Android 6.0+):与 Universal Links 理念一致,使用 HTTPS + assetlinks.json (或 Digital Asset Links )在服务器证明域名与 App 的关联,系统可无歧义地将链接打开到指定 App(不弹出「用哪个应用打开」)。
二者都是可验证的、基于域名的深链接方案,相比 URL Scheme 更安全、可突破部分容器对自定义 Scheme 的拦截,且与 Web 和 SEO 友好。
1.2 知识结构(思维导图)
二、整体校验与打开流程(泳道图)
三、iOS Universal Links 实现详解
3.1 服务器配置:apple-app-site-association(AASA)
在域名根路径或 .well-known 下提供 apple-app-site-association (无后缀),可通过以下两种 URL 访问:
https://yourdomain.com/apple-app-site-associationhttps://yourdomain.com/.well-known/apple-app-site-association
内容示例(JSON,且需 HTTPS):
json
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourapp.bundleid",
"paths": [
"/app/*",
"/detail/*",
"/news/*"
]
}
]
}
}
appID:苹果开发者 Team ID + 点 + Bundle ID。paths:匹配路径,支持*通配符;前面加NOT表示排除(如NOT /admin/*)。
3.2 Xcode 配置:Associated Domains
- 在 Signing & Capabilities 中增加 Associated Domains。
- 添加条目:
applinks:yourdomain.com(不要写https://)。
makefile
applinks:yourdomain.com
3.3 App 内接收并交给路由(Swift)
AppDelegate(冷启动 / 从后台被链接唤起):
swift
// AppDelegate.swift
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return false }
return AppRouter.shared.handleOpenURL(url)
}
SceneDelegate(若使用 Scene 生命周期):
swift
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return }
AppRouter.shared.handleOpenURL(url)
}
统一路由解析 path(与 Scheme 共用一套路由表):
swift
// AppRouter 中增加对 HTTPS 的处理
func handleOpenURL(_ url: URL) -> Bool {
if url.scheme == "myapp" {
return handleSchemeURL(url) // 见 02-URL-Scheme 文档
}
if url.scheme == "https", url.host == "yourdomain.com" {
// 将 https://yourdomain.com/app/detail/1 转为 path: /app/detail/1
let path = url.path
let params = url.queryItems ?? [:]
return navigate(path: path, params: params)
}
return false
}
四、Android App Links 实现详解
4.1 服务器配置:assetlinks.json
在以下任一路径提供 assetlinks.json(HTTPS):
https://yourdomain.com/.well-known/assetlinks.json
内容示例:
json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourapp",
"sha256_cert_fingerprints": [
"AA:BB:CC:..."
]
}
}
]
sha256_cert_fingerprints 为签名的 SHA256(冒号分隔),可通过:
bash
keytool -list -v -keystore your.keystore
获取。调试时需加入 debug 签名的指纹。
4.2 AndroidManifest 配置
在接收链接的 Activity 的 <intent-filter> 中增加 autoVerify ,并保持 ACTION_VIEW 、BROWSABLE 、DEFAULT 与 https 的 data:
xml
<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="yourdomain.com"
android:pathPrefix="/app"
android:pathPattern="/detail/.*" />
</intent-filter>
安装或更新后,系统会访问 assetlinks.json 做验证;可在系统设置中查看该应用「打开支持的链接」状态。
4.3 Activity 中解析 Intent(Kotlin)
与普通深链接一致,通过 Intent.data 获取 Uri:
kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleDeepLink(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.let { handleDeepLink(it) }
}
private fun handleDeepLink(intent: Intent?) {
val uri = intent?.data ?: return
if (uri.scheme != "https" || uri.host != "yourdomain.com") return
val path = uri.path ?: return
val params = uri.queryParameterNames.associateWith { uri.getQueryParameter(it) ?: "" }
AppRouter.handle(this, path, params)
}
五、Flutter 实现详解
5.1 依赖与平台配置
pubspec.yaml:
yaml
dependencies:
app_links: ^6.3.0
iOS:在 Xcode 中配置 Associated Domains(applinks:yourdomain.com)。
Android:如上节配置 assetlinks.json 与 intent-filter android:autoVerify="true"。
5.2 监听初始链接与后续链接(Dart)
dart
// lib/deep_link_handler.dart
import 'package:app_links/app_links.dart';
class DeepLinkHandler {
static final AppLinks _appLinks = AppLinks();
static Future<void> init() async {
// 冷启动:通过 Universal Link / App Link 打开
final initialUri = await _appLinks.getInitialLink();
if (initialUri != null) {
_routeFromUri(initialUri);
}
// 热启动:App 在后台时点击链接
_appLinks.uriLinkStream.listen((Uri uri) {
_routeFromUri(uri);
});
}
static void _routeFromUri(Uri uri) {
// 同时支持 myapp:// 与 https://yourdomain.com
if (uri.scheme == 'myapp') {
GlobalRouter.navigateFromScheme(uri);
return;
}
if (uri.scheme == 'https' && uri.host == 'yourdomain.com') {
GlobalRouter.navigate(uri.path, params: uri.queryParameters);
}
}
}
六、校验与打开时序图
七、AASA / assetlinks 校验流程(伪代码)
客户端不直接校验文件内容,由系统在安装/更新时拉取并缓存;以下描述服务端/运维视角 的校验逻辑与App 内收到 URL 后的处理。
markdown
服务端确保 AASA 可访问(iOS):
1. 文件路径: https://<domain>/apple-app-site-association 或 /.well-known/...
2. Content-Type 建议为 application/json
3. 无需 .json 后缀;HTTPS 且证书有效
App 内收到 Universal Link / App Link 后:
1. 系统已根据缓存的 AASA/assetlinks 将链接关联到本 App,并传入 URL
2. 从 URL 解析 path 与 query(与 Scheme 解析方式一致)
3. 若为 https,path = uri.path,params = uri.queryParameters
4. 调用统一 Router.navigate(path, params)
八、与 URL Scheme 的对比与选型
| 维度 | URL Scheme | Universal Links / App Links |
|---|---|---|
| URL 形式 | myapp://host/path |
https://yourdomain.com/path |
| 验证 | 无,易冲突 | 域名 + 文件校验,归属明确 |
| 未安装时 | 通常无反应 | 可在浏览器打开同一页面 |
| 容器限制 | 易被微信等拦截 | 多数场景可正常跳转 |
| 配置成本 | 低 | 需服务器与域名、签名配置 |
建议:App 内路由统一按 path + query 设计,对外同时支持 Scheme(兼容旧链、简单场景)与 Universal Links / App Links(新链、分享、合规与体验)。
8.1 各平台可验证 HTTPS 深链接支持
| 平台 | 支持情况 | 配置文件 / 备注 |
|---|---|---|
| iOS / MacOS | ✓ Universal Links | AASA,Associated Domains |
| Android | ✓ App Links | assetlinks.json,autoVerify |
| HarmonyOS | ✓ 支持 HTTPS 关联 | 需在工程与服务器配置关联 |
| Flutter / ReactNative | ✓ 依赖宿主 | 使用 iOS/Android 配置 + app_links / Linking |
| WinOS | 部分(无统一 AASA 类标准) | 多为自定义协议或启动参数 |
| WebApp | 不适用(URL 即深度) | 同源/多域路由 |
| WatchOS | 一般不单独配置 | 多与 iPhone 协同 |
九、小结
- Universal Links / App Links 通过 HTTPS + 服务器配置文件证明「域名归属」,实现可验证、无歧义的深链接。
- iOS :AASA 文件 + Associated Domains +
NSUserActivityTypeBrowsingWeb;Android :assetlinks.json +autoVerify+ Intent.data;Flutter :app_links的getInitialLink/uriLinkStream。 - App 内应将 path + query 统一交给同一套路由层(与 URL Scheme 共用路由表与拦截器),便于维护与扩展。
十、HTTPS 深链接统一入口示例(多端)
无论入口是 Scheme 还是 Universal Link / App Link,进入 App 后都转为 (path, params) 再交给同一 Router,便于维护。
iOS(Swift) :在 handleOpenURL 中区分 scheme,统一成 path:
swift
func handleOpenURL(_ url: URL) -> Bool {
let path: String
let params: [String: String]
if url.scheme == "https", url.host == "yourdomain.com" {
path = url.path
params = url.queryItems ?? [:]
} else if url.scheme == "myapp" {
path = "/" + (url.host ?? "page") + url.path
params = url.queryItems ?? [:]
} else { return false }
return AppRouter.shared.navigate(path: path, params: params)
}
Android(Kotlin):在 Activity 中统一处理 Intent.data:
kotlin
private fun handleDeepLink(intent: Intent?) {
val uri = intent?.data ?: return
val path = when {
uri.scheme == "https" && uri.host == "yourdomain.com" -> uri.path ?: return
uri.scheme == "myapp" -> "/${uri.host ?: "page"}${uri.path ?: ""}"
else -> return
}
val params = uri.queryParameterNames.associateWith { uri.getQueryParameter(it) ?: "" }
AppRouter.handle(this, path, params)
}
Flutter(Dart) :在 _routeFromUri 中已按 scheme 分支,最终都调用 GlobalRouter.navigate(path, params: params),无需重复实现。
参考文献
- Apple. Supporting Universal Links.
- Android. Verify Android App Links.
- Flutter. Deep linking.
- 《01-软件平台路由规则设计-总纲》§2.2、§6.