深度链接 #
一、深度链接概述 #
1.1 链接类型 #
| 类型 | 平台 | 说明 |
|---|---|---|
| URL Scheme | iOS/Android | 自定义协议,如 myapp:// |
| Universal Links | iOS | HTTPS链接,无缝跳转 |
| App Links | Android | HTTPS链接,无缝跳转 |
1.2 使用场景 #
- 分享链接打开应用特定页面
- 推送通知跳转
- 营销活动链接
- 第三方登录回调
二、URL Scheme #
2.1 iOS配置 #
xml
<!-- ios/App/App/Info.plist -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.company.myapp</string>
</dict>
</array>
2.2 Android配置 #
xml
<!-- android/app/src/main/AndroidManifest.xml -->
<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" />
</intent-filter>
</activity>
2.3 使用URL Scheme #
html
<!-- 网页链接 -->
<a href="myapp://profile/123">打开用户资料</a>
<a href="myapp://product?id=456">查看商品</a>
2.4 JavaScript处理 #
typescript
import { App } from '@capacitor/app';
// 监听URL打开
App.addListener('appUrlOpen', (data) => {
console.log('URL opened:', data.url);
// 解析URL
const url = new URL(data.url);
const path = url.pathname;
const params = url.searchParams;
// 根据路径导航
if (path.startsWith('/profile/')) {
const userId = path.split('/')[2];
router.push(`/profile/${userId}`);
} else if (path.startsWith('/product')) {
const productId = params.get('id');
router.push(`/product/${productId}`);
}
});
三、Universal Links (iOS) #
3.1 配置步骤 #
1. 开启Associated Domains #
- 登录Apple Developer
- 选择App ID
- 启用Associated Domains
2. Xcode配置 #
text
Target → Signing & Capabilities → + Capability → Associated Domains
添加: applinks:example.com
3. 创建apple-app-site-association文件 #
json
// https://example.com/.well-known/apple-app-site-association
{
"applinks": {
"details": [
{
"appID": "TEAMID.com.company.myapp",
"paths": ["*"]
}
]
}
}
4. 服务器配置 #
nginx
# Nginx配置
location /.well-known/apple-app-site-association {
default_type application/json;
alias /var/www/.well-known/apple-app-site-association;
}
# 确保HTTPS
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
3.2 处理Universal Links #
swift
// ios/App/App/AppDelegate.swift
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL {
// 通知Capacitor
ApplicationDelegateProxy.shared.application(
application,
continue: userActivity,
restorationHandler: restorationHandler
)
return true
}
return false
}
}
typescript
// JavaScript处理
import { App } from '@capacitor/app';
App.addListener('appUrlOpen', (data) => {
// data.url: https://example.com/profile/123
handleDeepLink(data.url);
});
function handleDeepLink(url: string) {
const parsedUrl = new URL(url);
// 路由处理
switch (parsedUrl.pathname) {
case '/profile':
const userId = parsedUrl.searchParams.get('id');
router.push(`/profile/${userId}`);
break;
case '/product':
const productId = parsedUrl.searchParams.get('id');
router.push(`/product/${productId}`);
break;
default:
router.push(parsedUrl.pathname);
}
}
四、App Links (Android) #
4.1 配置步骤 #
1. 创建assetlinks.json #
json
// https://example.com/.well-known/assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.company.myapp",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:CE:..."
]
}
}]
2. 获取SHA256指纹 #
bash
keytool -list -v -keystore my-release-key.jks
3. AndroidManifest配置 #
xml
<activity android:name=".MainActivity" ...>
<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" />
<data android:host="example.com" />
</intent-filter>
</activity>
4.2 处理App Links #
java
// MainActivity.java
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri != null) {
// 处理深度链接
handleDeepLink(uri.toString());
}
}
}
typescript
// JavaScript处理
import { App } from '@capacitor/app';
App.addListener('appUrlOpen', (data) => {
handleDeepLink(data.url);
});
五、深度链接服务 #
5.1 封装深度链接服务 #
typescript
// src/services/deeplink.service.ts
import { App } from '@capacitor/app';
import { Capacitor } from '@capacitor/core';
type DeepLinkHandler = (params: Record<string, string>) => void;
interface DeepLinkRoute {
path: string | RegExp;
handler: DeepLinkHandler;
}
class DeepLinkService {
private routes: DeepLinkRoute[] = [];
private initialized = false;
async init(): Promise<void> {
if (this.initialized) return;
// 监听应用打开URL
await App.addListener('appUrlOpen', (data) => {
this.handleUrl(data.url);
});
// 检查冷启动时的URL
const launchUrl = await this.getLaunchUrl();
if (launchUrl) {
this.handleUrl(launchUrl);
}
this.initialized = true;
}
private async getLaunchUrl(): Promise<string | null> {
try {
const { url } = await App.getLaunchUrl();
return url;
} catch {
return null;
}
}
register(path: string | RegExp, handler: DeepLinkHandler): void {
this.routes.push({ path, handler });
}
private handleUrl(url: string): void {
console.log('Deep link URL:', url);
try {
const parsedUrl = new URL(url);
const pathname = parsedUrl.pathname;
const params = Object.fromEntries(parsedUrl.searchParams);
// 匹配路由
for (const route of this.routes) {
if (this.matchPath(route.path, pathname)) {
route.handler(params);
return;
}
}
console.warn('No handler for deep link:', pathname);
} catch (error) {
console.error('Error parsing deep link:', error);
}
}
private matchPath(pattern: string | RegExp, pathname: string): boolean {
if (pattern instanceof RegExp) {
return pattern.test(pathname);
}
return pathname === pattern || pathname.startsWith(pattern + '/');
}
}
export const deepLinkService = new DeepLinkService();
5.2 使用示例 #
typescript
// 初始化
await deepLinkService.init();
// 注册路由
deepLinkService.register('/profile', (params) => {
const userId = params.id;
router.push(`/profile/${userId}`);
});
deepLinkService.register('/product', (params) => {
const productId = params.id;
router.push(`/product/${productId}`);
});
deepLinkService.register(/^\/article\/\d+$/, (params) => {
// 匹配 /article/123 格式
router.push(window.location.pathname);
});
六、测试深度链接 #
6.1 iOS测试 #
bash
# 测试URL Scheme
xcrun simctl openurl booted "myapp://profile/123"
# 测试Universal Links
xcrun simctl openurl booted "https://example.com/profile/123"
6.2 Android测试 #
bash
# 测试URL Scheme
adb shell am start -a android.intent.action.VIEW \
-d "myapp://profile/123"
# 测试App Links
adb shell am start -a android.intent.action.VIEW \
-d "https://example.com/profile/123"
6.3 验证配置 #
bash
# iOS验证
curl -v https://example.com/.well-known/apple-app-site-association
# Android验证
curl -v https://example.com/.well-known/assetlinks.json
七、延迟深度链接 #
7.1 实现延迟深度链接 #
typescript
class DeferredDeepLinkService {
private storageKey = 'deferred_deep_link';
async saveDeferredLink(url: string): Promise<void> {
await Preferences.set({
key: this.storageKey,
value: JSON.stringify({
url,
timestamp: Date.now()
})
});
}
async getDeferredLink(): Promise<string | null> {
const { value } = await Preferences.get({ key: this.storageKey });
if (!value) return null;
const data = JSON.parse(value);
// 检查是否过期(24小时)
if (Date.now() - data.timestamp > 24 * 60 * 60 * 1000) {
await this.clearDeferredLink();
return null;
}
return data.url;
}
async clearDeferredLink(): Promise<void> {
await Preferences.remove({ key: this.storageKey });
}
}
八、总结 #
8.1 配置要点 #
| 平台 | 配置文件 | 验证文件 |
|---|---|---|
| iOS URL Scheme | Info.plist | - |
| iOS Universal Links | Associated Domains | apple-app-site-association |
| Android URL Scheme | AndroidManifest.xml | - |
| Android App Links | AndroidManifest.xml | assetlinks.json |
8.2 下一步 #
了解深度链接后,让我们学习 后台任务!
最后更新:2026-03-28