目的
本文檔用於幫助客戶一步一步實施深度鏈接,不論是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"
更多信息請參考官網文檔