深度链接 Deep Link
目的
本文档用于帮助客户一步一步实施深度链接,不论是Android还是iOS系统,自定义的、或者更高 级的App Link / Universal Link。希望帮助客户实施用户召回这个方案上扫清障碍。
背景介绍
2015年的Google I/O大会上推出的Universal App Campaigns距今已经有4年多了,客户伴随着 UAC1.0, 2.0, 2.5, 3.0的产品迭代,或多或少完成了自己获取新客户的需求,当下一个迫切的需求 就是如何召回老用户继续使用我的App。基于这样的业务需求,Google今年推出了App Campaigns for Engagement这款产品来帮助广告主召回老用户。要想使用这款产品,有三个条 件是必不可少的:
- 深度链接
- 创建受众用户列表
- 做好转化追踪
其中深度链接是这三个条件当中相对来说比较复杂的一环,所以本文档会一步一步交给大家如何 正确实施适用于Google ACe的深度链接。
概览
文档会分别介绍Android和iOS基于Custom Scheme的深度链接和App Link / Universal Link的技术 方案,并且提供给开发者如何去验证你的深度连接是否工作。
深度链接(Deep Link)
什么是深度链接?通俗地讲,他是指移动端应用在处理特定URI的时候可以直接跳转到对应的内容 页或出发特定逻辑,而不仅仅是启动App。深度链接的好处是可以提高用户的打开转化率,便于 用户访问,提高用户体验,传播更方便,为营销提供了一种全新的工具。 目前处理深度链接主要有两种方式:Custom URI Schemes和App / Universal Links. 以下也会分别 介绍不同技术方案的实现方式和优缺点。
自定义 URI Schemes
在App / Universal Links出现以前,Android和iOS都采用的URI Schemes的方式来处理深度。使用 这种方式实现的有点是易于开发,可同时适用于Android和iOS系统。缺点是不安全,技术比较落 后,用户体验不是特别好。
iOS Custom URI Scheme
要想在iOS上实现自定义URI scheme,需要完成以下三个步骤:
- 定义你的app的URL格式
- 注册你的scheme
- 在app内处理打开URL的业务逻辑
如何定义URL
URL一定要以scheme名称开头,既然是自定义URI scheme,按就要保证自己app的scheme是独 一无二的,不要跟同一设备里的其他app scheme重名,例如我们是做photo的开发者,可以定义 scheme为myphotoapp。
注册URL Scheme
注册你的URL scheme,以便让app知道哪些URL是可以被app直接打开的。在Xcode中打开 info.plist文件
- 在Information Property List下添加URL types
- 添加URL Schemes,例如:myphotoapp
- 添加URL identifier,例如:com.example.myphotoapp
- 添加Document Role, Viewer, 如图:
在应用内处理URL
当以上都组做完之后,就可以在应用内开发处理URL的业务逻辑了。在iOS里是使用 application(_:open:options:)方法来实现的。实例代码如下:
1 func application(_ application: UIApplication, 2 open url: URL, 3 options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool { 4 // Determine who sent the URL. 5 let sendingAppID = options[.sourceApplication] 6 print("source application = \(sendingAppID ?? "Unknown")") 7 // Process the URL. 8 guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true), 9 let albumPath = components.path, 10 let params = components.queryItems else { 11 print("Invalid URL or album path missing") 12 return false 13 } 14 if let photoIndex = params.first(where: { $0.name == "index" })?.value { 15 print("albumPath = \(albumPath)") 16 print("photoIndex = \(photoIndex)") 17 return true 18 } else { 19 print("Photo index missing") 20 return false 21 } 22 }
Android Custom URI Scheme
Android系统的深度链接需要完成以下步骤:
- 在manifest中添加intent filter
- 解析intent filter内容
- 测试深度链接
在manifest中添加Intent Filters
要想开发一个可以打开你的应用的连接,需要在manifest文件中添加包含elements和attribute的 值到intent filter里:
<action>: 请使用ACTION_VIEW intent action,这样才能让Google Search搜索到。
<category>: 请包含BROWSABLE和DEFAULT category, 这样才能让你的app被浏览器访问到。
<date>: data tag里必须包含android:scheme 例如一个深度链接的URI是: example://gizmos,那么intent filter应该是这样的
1 <activity 2 android:name="com.example.android.GizmosActivity" 3 android:label="@string/title_gizmos" > 4 <intent-filter android:label="@string/filter_view_example_gizmos"> 5 <action android:name="android.intent.action.VIEW" /> 6 <category android:name="android.intent.category.DEFAULT" /> 7 <category android:name="android.intent.category.BROWSABLE" /> 8 <!-- Accepts URIs that begin with "example://gizmos” --> 9 <data android:scheme="example" 10 android:host="gizmos" /> 11 </intent-filter> 12 </activity>
解析Intent Filter内容
通过调用getData()和getAction()可以获取Intent的Data和Action。请参考以下Java代码示例:
1 @Override 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.main); 5 6 Intent intent = getIntent(); 7 String action = intent.getAction(); 8 Uri data = intent.getData(); 9 }
测试深度链接
可以使用Android Debug Bridge和Activity Manager工具来测试深度链接是否可以实现正确的 app操作。可以在设备或者模拟器上运行adb命令,示例如下:
1 $ adb shell am start 2 -W -a android.intent.action.VIEW 3 -d <URI> <PACKAGE>
更多信息请参考 官方文档
Custom Scheme 测试工具
1. 安装app并打开至少一次。
2. 用手机浏览器打开https://appdevdeeplink.firebaseapp.com/
3. 输入你的Custom Scheme URL,点击“Generate Deep Link”
4. 点击生成的蓝色按钮。如果深度链接配置正确,将可以打开你的app。
App / Universal Links
随着技术的演进,2015年的时候,Android和iOS分别发布新的技术来支持深度链接,Android阵 营是App Links,iOS阵营是Universal Links,才使得开发深度链接更加便捷、安全和高效。就应 用开发的安全性和面向未来的兼容性而言,我们强烈推荐采用App / Universal Links的方式开发深 度链接。
iOS
实现iOS通用链接(深度链接)需要有一个域名,并且网站是支持HTTPS协议。打开深度链接时, iOS 会检查该设备已安装的应用是否在这一域中被否注册了,如果已注册,App 将立即启动,无 需加载网页。否则,网页 URL (可以简单重定向到 App Store)将在 Safari 浏览器中加载。
配置iOS深度链接需要完成以下3个步骤:
- 创建apple-app-site-association文件,其中包含app可以处理URL,内容为JSON格式。
- 将apple-app-site-association上传至你的服务器,将文件放在根目录或 .well-known子目 录下。
- 准备好你的app。
创建并上传Association File
每个包含不同内容的域名需要一个单独的apple-app-site-association 文件,没有后缀名。iOS版本 9.3.1以上的,未压缩的apple-app-site-association 文件大小不能超过128kb。 在apple-app-site-association 文件中,你需要指定使用深度链接的路径(path)。以下示例展示了3条 路径均要被处理为深度链接的情况:
1 { 2 "applinks": { 3 "apps": [], 4 "details": [ 5 { 6 "appID": "9JA89QQLNQ.com.apple.wwdc", 7 "paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"] 8 }, 9 { 10 "appID": "ABCD1234.com.apple.wwdc", 11 "paths": [ "*" ] 12 } 13 ] 14 } 15 }
因为系统会按path数组的顺序评估,因而需要将高优先级的path放在前面。有多种指定path的方法:
- 用*来代替所有网页
- 指定一个特定的URL, 比如加上 /wwdc/news/
- 除了可以用*之外,还可以用?来匹配单一字符
创建完 apple-app-site-association文件之后,需要将它上传至服务器根目录或者 .well-known 目录 下。这个文件需要可以直接通过HTTPS访问而不经过任何跳转。
准备App来测试深度链接
当一个用户点击深度链接的时候,就会进入app了。iOS会触发app来发送NSUserActivity请求来 获取此app打开的方法。在你的app里使用深度链接,你需要完成下列步骤:
- Entitlement文件包含你的域名信息,将此文件加入到app support中。
- 为app指定收到NSUserActivity请求时的响应处理。
首先打开工程配置中的Associated Domains,并添加支持的域名,前缀是applinks:,例如 applinks:www.mywebsite.com.
指定好域名之后,你需要采用UIApplicationDelegate方法( application:continueUserActivity:restorationHandler:)使得你的app可以收到和处理请求。 当用户点击深度链接之后,会收到NSUserActivity 类型为activityType,值为 NSUserActivityTypeBrowsingWeb的请求。参考代码如下
1 @UIApplicationMain 2 class AppDelegate: UIResponder, UIApplicationDelegate { 3 4 var window: UIWindow? 5 func application(_ application: UIApplication, continue userActivity: NSUserActivity, 6 restorationHandler: @escaping ([Any]?) -> Void) -> Bool { 7 if userActivity.activityType == NSUserActivityTypeBrowsingWeb { 8 let webpageURL = userActivity.webpageURL 9 print("点击的链接是:\(webpageURL)") 10 11 //...... 12 } 13 return true 14 }
更多信息请参考官方文档
iOS 深度链接测试
1. 打开 https://search.developer.apple.com/appsearch-validation-tool/
2. 输入你的深度链接URL,并点击搜索按钮
3. 查看你的报告结果 (以https://maps.google.com为例)
Android
注:
url scheme =对应android帮助文档中deeplink和深层链接
app links = 对应android帮助文档中applinks 和 应用连接
应用链接是一种深层链接,它们基于已验证属于您网站的网站网址。因此,点击某个此 类链接会立即打开您的应用(如已安装),并且弹出选择应用程序的对话框。自定义scheme的 深度链接是一种 intent 过滤器,可让用户直接进入 Android 应用中的特定 Activity。点击此类链 接可能会打开一个应用程序选择对话框,该对话框可以让用户从多个能够处理给定网址的应用 (包括您的应用)中选择一个。
例如,图 显示的是在用户点击地图链接后打开的对话框,该对 话框会询问用户是在 Google 地图中还是 Chrome 中打开此链接
以下列表介绍了App Link与自定义URI Scheme的关系:
Android系统的深度链接需要完成以下步骤:
- 在manifest中添加intent filter
- 解析intent filter内容
- 声明网站与应用
- 测试深度链接
当app收到网页链接访问请求时,安卓系统会优先用用户偏好的app打开此链接。若无此偏好设置 ,则会用唯一可以使用的app打开,最后才会让用户从对话框列表中自己选择。
Manifest文件的配置和Intent Handler定义了app和网页之间的链接和活动内容。
在manifest中添加Intent Filters
下面的XML代码展示了如何添Iintent Filter来实现深度链接,由此可以实现打开URI “http://www.example.com/gizmos”。
1 <activity 2 android:name="com.example.android.GizmosActivity" 3 android:label="@string/title_gizmos" > 4 <intent-filter android:label="@string/filter_view_http_gizmos"> 5 <action android:name="android.intent.action.VIEW" /> 6 <category android:name="android.intent.category.DEFAULT" /> 7 <category android:name="android.intent.category.BROWSABLE" /> 8 <!-- Accepts URIs that begin with "http://www.example.com/gizmos” --> 9 <data android:scheme="http" 10 android:host="www.example.com" 11 android:pathPrefix="/gizmos" /> 12 <!-- note that the leading "/" is required for pathPrefix--> 13 </intent-filter> 14 </activity>
解析Intent Filter内容
通过调用getData()和getAction()可以获取Intent的Data和Action。请参考以下Java代码示例:
1 @Override 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.main); 5 6 Intent intent = getIntent(); 7 String action = intent.getAction(); 8 Uri data = intent.getData(); 9 }
声明网站与应用
Android 应用链接是一种特殊类型的深层链接,可让您的网站网址直接在您的 Android 应 用中打开相应内容(无需用户选择应用)。
要向应用添加 Android 应用链接,请定义使用 HTTP 网址打开应用内容的 intent 过滤器 (如解析Intent Filter中所述),并验证您是否为相关应用和网站网址的所有者。如果系 统成功验证您是网址所有者,则会自动将这些网址 intent 路由到您的应用。
要验证您对应用和网站的所有权,您需要执行以下步骤:
- 在清单中请求自动验证应用链接。这样即可向 Android 系统说明其应该验证您的 应用是否属于 intent 过滤器中使用的网址网域。
- 通过在以下位置托管 Digital Asset Links JSON 文件,声明您的网站和 intent 过滤 器之间的关系:https://domain.name/.well-known/assetlinks.json
请求应用链接验证
要为您的应用启用链接处理验证,请在应用清单内的网址 intent 过滤器中设置 android:autoVerify="true",所选的过滤器可以是具有 android.intent.action.VIEW intent 操作 并属于 android.intent.category.BROWSABLE intent 类别的任意过滤器,如以下清单代码段 所示:
1 <activity ...> 2 <intent-filter android:autoVerify="true"> 3 <action android:name="android.intent.action.VIEW" /> 4 <category android:name="android.intent.category.DEFAULT" /> 5 <category android:name="android.intent.category.BROWSABLE" /> 6 <data android:scheme="http" android:host="www.example.com" /> 7 <data android:scheme="https" /> 8 </intent-filter> 9 </activity>
如果您的任一 intent 过滤器中存在 android:autoVerify="true",则在搭载 Android 6.0 及更 高版本的设备上安装您的应用会导致系统尝试验证与应用的任何 intent 过滤器中的网址 相关联的所有主机。验证涉及以下方面:
- 系统会检查所有包含以下各项的 intent 过滤器:
-
- 操作:android.intent.action.VIEW
- 类别:android.intent.category.BROWSABLE 和 android.intent.category.DEFAULT
- 网络协议:http 或 https
-
- 对于在上述 intent 过滤器中找到的每个唯一主机名,Android 会在相应网站上查询 位于 https://hostname/.well-known/assetlinks.json 的 Digital Asset Links 文件。
仅当系统为清单中的所有主机找到匹配的 Digital Asset Links 文件后,才会将您的应用确 立为处理指定网址模式的默认处理程序。
支持多个主机的应用链接
系统必须能够对照每个相应网域上托管的 Digital Asset Links 文件验证应用网址 intent 过 滤器的 元素中指定的每个主机。如果任何验证失败了,应用便无法通过验证,因 此不能成为应用 intent 过滤器中定义的任何网址模式的默认处理程序。然后,系统会默 认采用标准行为来解析相应 intent。
例如,如果在 https://www.example.com/.well-known/assetlinks.json 或 https://www.example.net/.well-known/assetlinks.json 未找到 assetlinks.json 文件,则具有以 下 intent 过滤器的应用无法通过验证:
1 <application> 2 3 <activity android:name=”MainActivity”> 4 <intent-filter android:autoVerify="true"> 5 <action android:name="android.intent.action.VIEW" /> 6 <category android:name="android.intent.category.DEFAULT" /> 7 <category android:name="android.intent.category.BROWSABLE" /> 8 <data android:scheme="http" android:host="www.example.com" /> 9 <data android:scheme="https" /> 10 </intent-filter> 11 </activity> 12 <activity android:name=”SecondActivity”> 13 <intent-filter> 14 <action android:name="android.intent.action.VIEW" /> 15 <category android:name="android.intent.category.DEFAULT" /> 16 <category android:name="android.intent.category.BROWSABLE" /> 17 <data android:scheme="https" android:host="www.example.net" /> 18 </intent-filter> 19 </activity> 20 21 </application> 22
请注意,同一 intent 过滤器中的所有 元素会合并在一起以涵盖合并后属性的所有 变体。例如,上面的第一个 intent 过滤器包含一个仅声明 HTTPS 协议的 元素。但 是,该元素与其他 元素组合在一起,所以此 intent 过滤器支持 http://www.example.com 和 https://www.example.com。因此,如果您想要定义 URI 协议和 网域的特定组合,则必须创建单独的 intent 过滤器。
支持多个子网域的应用链接
Digital Asset Links 协议将 intent 过滤器中的子网域视为唯一的独立主机。因此,如果您的 intent 过滤器列出多个包含不同子网域的主机,您必须在每个网域上分别发布一个有效的 assetlinks.json。例如,以下 Intent 过滤器包含 www.example.com 和 mobile.example.com 作为接受的 intent 网址主机。因此,必须在 https://www.example.com/.well-known/assetlinks.json 和 https://mobile.example.com/.well-known/assetlinks.json 上发布有效的 assetlinks.json。
1 <application> 2 <activity android:name=”MainActivity”> 3 <intent-filter android:autoVerify="true"> 4 <action android:name="android.intent.action.VIEW" /> 5 <category android:name="android.intent.category.DEFAULT" /> 6 <category android:name="android.intent.category.BROWSABLE" /> 7 <data android:scheme="https" android:host="www.example.com" /> 8 <data android:scheme="https" android:host="mobile.example.com" /> 9 </intent-filter> 10 </activity> 11 </application> 12
或者,如果您使用通配符(例如 *.example.com
)声明主机名,则必须在根主机名 (example.com
) 上发布 assetlinks.json
文件。例如,只要将 assetlinks.json
文件发布到 https://example.com/.well- known/assetlinks.json
,具有以下 intent 过滤器的应用就会通过针对 example.com
的任何子域名(如 foo.example.com
)的验证:
1 <application> 2 <activity android:name=”MainActivity”> 3 <intent-filter android:autoVerify="true"> 4 <action android:name="android.intent.action.VIEW" /> 5 <category android:name="android.intent.category.DEFAULT" /> 6 <category android:name="android.intent.category.BROWSABLE" /> 7 <data android:scheme="https" android:host="*.example.com" /> 8 </intent-filter> 9 </activity> 10 </application>
您必须在网站上发布 Digital Asset Links JSON 文件,以指示与网站相关联的 Android 应用并验证应用的网址 intent。JSON 文件使用下列字段标识关联的应用:
声明网站关联性
package_name:在应用的 build.gradle 文件中声明的应用 ID。
sha256_cert_fingerprints:应用的签名证书的 SHA256 指纹。您可以利用 Java 密钥工具,通过以下命令生成该指纹:
$ keytool -list -v -keystore my-release-key.keystore
此字段支持多个指纹,这些指纹可用于支持不同版本的应用,例如调试版 build 和正式版 build。
以下 assetlinks.json 示例文件可为 com.example Android 应用授予链接打开权限:
1 [{ 2 "relation": ["delegate_permission/common.handle_all_urls"], 3 "target": { 4 "namespace": "android_app", 5 "package_name": "com.example", 6 "sha256_cert_fingerprints": 7 ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"] 8 } 9 }]
将网站与应用关联
需要将网站在assetlinks.json文件中声明与应用的关联性。一下文件展示了如何声明 example.com与app1的关联性。 https://www.example.com/.well-known/assetlinks.json
1 [{ 2 "relation": ["delegate_permission/common.handle_all_urls"], 3 "target": { 4 "namespace": "android_app", 5 "package_name": "com.example", 6 "sha256_cert_fingerprints": 7 ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"] 8 } 9 }]
发布JSON验证文件
您必须在以下位置发布 JSON 验证文件: https://domain.name/.well-known/assetlinks.json
请确保以下几点:
- 使用内容类型 application/json 发布 assetlinks.json 文件。
- 无论应用的 intent 过滤器是否将数据协议声明为 HTTPS,都必须可通过 HTTPS 连 接访问 assetlinks.json 文件。
- assetlinks.json 文件必须可直接访问,没有任何重定向(无 301 或 302 重定向), 并且漫游器可访问(您的 robots.txt 必须允许抓取 /.well-known/assetlinks.json)。
- 如果您的应用链接支持多个主机网域,则必须在每个网域上分别发布 assetlinks.json 文件。
- 请勿发布清单文件中的开发/测试网址无法供公众访问的应用(例如,任何只可通 过 VPN 访问的应用)。
Android 应用链接测试
可以使用Android Debug Bridge和Activity Manager工具来测试深度链接是否可以实现正确的 app操作。可以在设备或者模拟器上运行adb命令,示例如下:
1 adb shell am start -a android.intent.action.VIEW \ 2 -c android.intent.category.BROWSABLE \ 3 -d "http://domain.name:optional_port"
更多信息请参考官网文档