設定、實作及驗證 Android 應用程式連結

1. 事前準備

使用者點選深層連結的主要目的,是獲得他們想看到的內容。深層連結具備所有協助使用者達成這項目的需要的功能。Android 會處理下列類型的連結:

  • 深層連結:是能夠採用任何配置的 URI,可將使用者導向應用程式的特定部分。
  • 網頁連結:含有 HTTP 和 HTTPS 配置的深層連結。
  • Android 應用程式連結:含有 HTTP 和 HTTPS 配置,且包含 android:autoVerify 屬性的網頁連結。

如想進一步瞭解深層連結、網頁連結和 Android 應用程式連結,請參閱 Android 說明文件,以及 YouTubeMedium 上的速成課程。

如果您已經熟悉所有技術詳情,歡迎參閱隨附的網誌文章,只要幾個步驟就能完成設定。

程式碼研究室目標

本程式碼研究室會引導您逐步完成內含 Android 應用程式連結的應用程式,包括設定、實作和驗證程序的最佳做法。

Android 應用程式連結的其中一項優點為十分安全,意思是沒有任何未經授權的應用程式可以處理您的連結。Android 作業系統必須驗證您擁有網站的連結,確認是否可將連結視為 Android 應用程式連結。這項程序稱為網站關聯

本程式碼研究室著重於擁有網站和 Android 應用程式的開發人員。Android 應用程式連結可將應用程式和網站完美整合,提供更優質的使用者體驗。

必要條件

課程內容

  • 瞭解設計 Android 應用程式連結網址的最佳做法。
  • 在 Android 應用程式中設定所有深層連結。
  • 瞭解路徑萬用字元 (pathpathPrefixpathPatternpathAdvancePattern)。
  • 瞭解 Android 應用程式連結驗證程序,包括上傳 Google Digital Asset Links (DAL) 檔案、Android 應用程式連結手動驗證程序,以及 Play 管理中心的深層連結資訊主頁。
  • 建構 Android 應用程式,其中含有不同地點的多家餐廳資訊。

網頁版餐廳應用程式完成後的外觀。 Android 版餐廳應用程式完成後的外觀。

軟硬體需求

  • Android Studio Dolphin (2021.3.1) 以上版本。
  • 代管 Google Digital Asset Link (DAL) 檔案的網域 (選用:參閱這篇網誌文章,有助於快速完成準備作業)。
  • 選用:Google Play 管理中心開發人員帳戶。這可讓您透過另一種方法對 Android 應用程式連結設定進行偵錯。

2. 設定代碼

建立空白的 Compose 應用程式

如要開始進行 Compose 專案,請按照下列步驟操作:

  1. 在 Android Studio 中,依序選取「File」>「New」>「New Project」

在「File」選單中依序選取以下路徑:「New」以及「New Project」。

  1. 從可用的範本中選取「Empty Compose Activity」

Android Studio 的「New」專案互動視窗,已選取「Empty Compose Activity」。

  1. 按一下「Next」,然後設定專案,並命名為「Deep Links Basics」。請務必為「Minimum SDK」選擇至少 API 級別 21 以上版本,也就是 Compose 支援的最低 API 版本。

Android Studio 的新專案設定互動視窗,包含以下選單值和選項。「Name」的值為「Deep Links Basics」。「Package Name」的值為「com.devrel.deeplinksbasics」。「Save location」為預設值。「Language」的值為「Kotlin」。「Minimum SDK」的值為「API 21」。

  1. 按一下「Finish」,然後等待專案產生。
  2. 啟動應用程式。確保應用程式為執行狀態。系統應該會是空白畫面,其中顯示「Hello Android!」訊息。

空白的 Android 應用程式編寫畫面,其中顯示下列文字:「Hello Android」。

程式碼研究室的解決方案

您可以到 GitHub 取得本程式碼研究室的解決方案程式碼:

git clone https://github.com/android/deep-links

或者,您也可以將存放區下載為 ZIP 檔案:

首先,請前往 deep-links-introduction 目錄。您可以在「solution」目錄中找到該應用程式。建議您以自己的步調,按照程式碼研究室的說明逐步操作,並視需要查看解決方案。在本程式碼研究室的學習過程中,我們會為您提供要新增到專案的程式碼片段。

3. 檢查深層連結導向的網址設計

符合 REST 標準的 API 設計

連結是網頁開發中的重要部分,而連結設計則是透過無數次疊代產生的各種標準。建議您檢查並套用網頁開發連結設計標準,這麼做可讓連結更方便使用及維護。

其中一項標準為 REST (具象狀態傳輸),這是一種用於建構網路服務 API 的架構。開放式 API 是將 REST API 標準化的計畫。此外,您也可以使用 REST 設計深層連結的網址。

請注意,您並非在建構 Web 服務。本節只會著重說明網址設計。

設計網址

首先,檢查網站中產生的網址,瞭解這些網址在 Android 應用程式中代表的意義:

  • /restaurants 會列出您管理的所有餐廳。
  • /restaurants/:restaurantName 會顯示單一餐廳的詳細資料。
  • /restaurants/:restaurantName/orders 會顯示餐廳的訂單。
  • /restaurants/:restaurantName/orders/:orderNumber 會顯示餐廳中的特定訂單。
  • /restaurants/:restaurantName/orders/latest 會顯示餐廳中的最新訂單。

網址設計的重要性

Android 的意圖篩選器會處理其他應用程式元件中的動作,也會用來擷取網址。定義用來擷取網址的意圖篩選器時,必須採用依賴路徑前置字串和簡易萬用字元的結構。以下範例說明餐廳網站上現有網址的組成結構:

https://example.com/pawtato-3140-Skinner-Hollow-Road

雖然這個網址指定您的應用程式及其位置,但在為 Android 定義意圖篩選器以取得網址時,該路徑可能會產生問題,因為應用程式是以不同的餐廳網址為基礎,如下所示:

https://example.com/rawrbucha-2064-carriage-lane

https://example.com/pizzabus-1447-davis-avenue

使用路徑和萬用字元定義意圖篩選器來擷取這些網址時,可以使用類似 https://example.com/* 的路徑,基本上也能正常運作。儘管如此,這項問題依然無法確實解決,因為網站的不同部分還有其他現有路徑,例如:

外送頁面:https://example.com/deliveries

管理頁面:https://example.com/admin

您可能不想讓 Android 擷取這些網址,因為其中部分網址可能為內部網址,但定義的意圖篩選器 https://example.com/* 會擷取這些網址,包括不存在的網址。此外,當使用者點選其中一個網址時,系統可能會在瀏覽器 (Android 12 以上版本) 中開啟網址,或是顯示消歧對話方塊 (Android 12 以下版本)。在設計中這並非預期行為。

現在,Android 可使用路徑前置字串解決這個問題,但必須重新設計網址,從:

https://example.com/*

改為:

https://example.com/restaurants/*

新增階層式巢狀結構,可讓意圖篩選器明確定義,並且讓 Android 擷取您指定的網址。

網址設計最佳做法

以下是收集自公開式 API 的最佳做法,適用於深層連結:

  • 將網址設計重點放在連結顯示的業務實體。舉例來說,電子商務實體的重點是「客戶」和「訂單」。旅遊實體的重點是「票券」和「航班」。在餐廳應用程式和網站中,您將使用「餐廳」和「訂單」
  • 大部分的 HTTP 方法 (GET、POST、DELETE 和 PUT) 都是動詞,說明發出的要求,但針對網址中的端點使用動詞會令人困惑。
  • 如要描述集合,請使用實體的複數,例如 /restaurants/:restaurantName。這樣可讓網址更方便閱讀及維護。以下是每個 HTTP 方法的範例:

GET /restaurants/pawtato

POST /restaurants

DELETE /restaurants

PUT /restaurants/pawtato

每個網址都十分簡單易懂。請注意,本程式碼研究室不會說明網路服務 API 的設計,以及每個方法的作用。

  • 使用邏輯巢狀結構將含有相關資訊的網址分組。舉例來說,其中一間餐廳的網址,可以新增正在處理中的訂單:

/restaurants/1/orders

4. 檢閱資料元素

AndroidManifest.xml 檔案是 Android 系統的重要部分,會將應用程式資訊提供給 Android 建構工具、Android 作業系統和 Google Play。

您必須使用 3 個主要標記,為深層連結定義意圖篩選器:<action><category><data>。本節的重點為 <data> 標記。

使用者點選連結後,<data> 元素會告知 Android 作業系統該連結的網址結構。您可以在意圖篩選器上使用的網址格式與結構如下:

<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>|<pathAdvancedPattern>|<pathSuffix>]

Android 會讀取、剖析及合併意圖篩選器中的所有 <data> 元素,以反映屬性的所有變化版本。例如:

AndroidManifest.xml

<intent-filter>
  ...
  <data android:scheme="http" />
  <data android:scheme="https" />
  <data android:host="example.com" />
  <data android:path="/restaurants" />
  <data android:pathPrefix="/restaurants/orders" />
</intent-filter>

Android 會擷取下列網址:

  • http://example.com/restaurants
  • https://example.com/restaurants
  • http://example.com/restaurants/orders/*
  • https://example.com/restaurants/orders/*

路徑屬性

path (適用於 API 1)

這項屬性會指定開頭為 /,且與意圖中「完整路徑」完全相符的完整路徑。舉例來說,android:path="/restaurants/pawtato" 只會比對 /restaurants/pawtato 網站路徑,如果路徑為 /restaurant/pawtato,則因為少了 s,系統會將這個網址視為不相符。

pathPrefix (適用於 API 1)

這項屬性會指定只與意圖路徑中「初始部分」相符的局部路徑。例如:

android:pathPrefix="/restaurants" 會比對餐廳路徑:/restaurants/pawtato/restaurants/pizzabus 等。

pathSuffix (適用於 API 31)

這項屬性會指定與意圖中路徑的「結尾部分」完全相符的路徑。例如:

android:pathSuffix="tato" 會比對所有結尾為「tato」的餐廳路徑,例如 /restaurants/pawtato/restaurants/corgtato

pathPattern (適用於 API 1)

這個屬性會指定與意圖中「包含萬用字元的完整路徑」相符的完整路徑:

  • 星號 (*) 會比對前面的半形字元出現 0 次到多次的序列。
  • .*:半形句號後面加上星號,會比對 0 個以上字元的任何序列。

例如:

  • /restaurants/piz*abus:這個模式會比對「pizzabus」餐廳,但也會比對名稱中含有 0 個以上 z 字元的餐廳,例如 /restaurants/pizzabus/restaurants/pizzzabus/restaurants/pizabus
  • /restaurants/.*:這個模式會比對所有包含 /restaurants 路徑的餐廳名稱 (例如 /restaurants/pizzabus/restaurants/pawtato),以及應用程式不知道的餐廳 (例如 /restaurants/wateriehall)。

pathAdvancePattern (適用於 API 31)

這項屬性會指定完整路徑,且該路徑會與「具有類似規則運算式模式的完整路徑」相符:

  • 句點 (.) 會比對任何字元。
  • 一組方括號 ([...]) 會比對字元範圍。這個組合也支援非 (^) 修飾符。
  • 星號 (*) 會比對上述模式 0 次以上。
  • 加號 (+) 會比對上述模式 1 次以上。
  • 大括號 ({...}) 代表模式可比對的次數。

這個屬性可視為 pathPattern 的延伸。可讓系統更靈活選擇要比對哪些網址,例如:

  • /restaurants/[a-zA-Z]*/orders/[0-9]{3} 會比對長度最多 3 位數的任何餐廳訂單。
  • /restaurants/[a-zA-Z]*/orders/latest 會比對應用程式中任何餐廳的最新訂單。

5. 建立深層連結和網頁連結

設有自訂配置的深層連結是最常見的深層連結類型,最容易實作,但也有缺點。這類連結不能由網站開啟。不過,凡是在資訊清單中宣告支援該配置的應用程式,都可以開啟深層連結。

您可以對 <data> 元素使用任何配置。舉例來說,本程式碼研究室會使用 food://restaurants/keybabs 網址。

  1. 在 Android Studio 中,將下列意圖篩選器加入資訊清單檔案:

AndroidManifest.xml

<activity ... >
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="food"/>
    <data android:path="/restaurants/keybabs"/>
  </intent-filter>
</activity>
  1. 如想確認應用程式能否開啟自訂配置的連結,請將以下內容新增至主要活動,在主畫面上顯示輸出內容:

MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Receive the intent action and data
        val action: String? = intent?.action;
        val data: Uri? = intent?.data;

        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    // Add a Column to print a message per line
                    Column {
                        // Print it on the home screen
                        Greeting("Android")
                        Text(text = "Action: $action")
                        Text(text = "Data: $data")
                    }
                }
            }
        }
    }
}
  1. 如要測試收到的意圖,請搭配下列指令使用 Android Debug Bridge (ADB):
adb shell am start -W -a android.intent.action.VIEW -d "food://restaurants/keybabs"

這個指令會透過 VIEW 動作啟動意圖,並使用您提供的網址做為資料。執行這個指令時,應用程式會啟動並接收意圖。請留意主畫面中文字部分的異動。第一行顯示「Hello Android!」訊息,第二行顯示意圖呼叫的動作,第三個動作則顯示意圖呼叫的網址。

請注意,在下圖中,Android Studio 底部的訊息提到 adb 指令已執行。在畫面右側,應用程式主畫面會顯示意圖資訊,表示已收到意圖。Android Studio 的全螢幕畫面會有下列開啟的分頁:「code view」、「emulator」和「terminal」。「code view」會顯示基本的 MainActivity.kt 檔案。「emulator」會顯示深層連結文字欄位,確認系統已成功收到意圖。「terminal」會顯示剛才在程式碼研究室中討論的 ADB 指令。

網頁連結是使用 httphttps 的深層連結,而不是自訂配置。

實作網頁連結時,請使用 /restaurants/keybabs/order/latest.html 路徑,代表餐廳收到的最新訂單。

  1. 使用現有的意圖篩選器調整資訊清單檔案。

AndroidManifest.xml

<intent-filter>
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="food"/>
  <data android:path="/restaurants/keybabs"/>

  <!-- Web link configuration -->
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="sabs-deeplinks-test.web.app"/>
  <data android:path="/restaurants/keybabs/order/latest.html"/>
</intent-filter>

由於這兩個路徑都已共用 (/restaurants/keybabs),建議將路徑置於相同的意圖篩選器底下,這樣子更容易實作,也更方便讀取資訊清單檔案。

  1. 測試網頁連結前,請重新啟動應用程式以套用新的變更。
  2. 使用相同的 ADB 指令啟動意圖,但本範例中我們會更新網址。
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/keybabs/orders/latest.html"

請注意,在螢幕截圖中,系統已收到意圖,且網路瀏覽器已開啟並顯示網站,此為 Android 12 以上版本的功能。Android Studio 完整檢視畫面包含以下分頁:「Code view」會顯示 AndroidManifest.xml 檔案,其中含有先前討論的意圖篩選器;「Emulator view」會顯示透過網頁連結開啟的網頁,該網頁指向網頁版餐廳應用程式;而「Terminal view」則會顯示用於網頁連結的 ADB 指令。

6. 設定 Android 應用程式連結

這些連結能提供最流暢的使用者體驗,因為使用者點選連結後,一定會直接前往應用程式,不會顯示消歧對話方塊。Android 應用程式連結是在 Android 6.0 版中實作,也是最具體的深層連結類型。這類連結是使用 http/https 配置和 android:autoVerify 屬性的網頁連結,讓應用程式成為所有相符連結的預設處理常式。實作 Android 應用程式連結的主要步驟有兩種:

  1. 使用合適的意圖篩選器更新資訊清單檔案。
  2. 新增網站關聯以進行驗證。

更新資訊清單檔案

  1. 如要支援 Android 應用程式連結,請在資訊清單檔案中將舊設定替換成以下內容:

AndroidManifest.xml

<!-- Replace deep link and web link configuration with this -->
<!-- Please update the host with your own domain -->
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="https"/>
  <data android:host="example.com"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>

此意圖篩選器會新增 android:autoVerify 屬性並將其設為「是」。這樣的話,Android 作業系統就能在應用程式安裝時和每次更新時驗證網域。

網站關聯

如要驗證 Android 應用程式連結,請在應用程式和網站之間建立關聯。您必須在網站上發布 Google Digital Asset Links (DAL) JSON 檔案,才能進行驗證。

Google DAL 是一種通訊協定和 API,定義其他應用程式和網站的可驗證陳述式。在本程式碼研究室中,您將在 assetlinks.json 檔案中建立關於 Android 應用程式的陳述式。範例如下:

assetlinks.json

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.devrel.deeplinksbasics",
    "sha256_cert_fingerprints":
   ["B0:4E:29:05:4E:AB:44:C6:9A:CB:D5:89:A3:A8:1C:FF:09:6B:45:00:C5:FD:D1:3E:3E:12:C5:F3:FB:BD:BA:D3"]
  }
}]

這個檔案可以儲存陳述式清單,但此範例只會顯示一個項目。每個陳述式都必須包含下列欄位:

  • Relation。說明系統宣告與目標相關的一或多個關係。
  • Target。這個陳述式適用的資產。可能是一或兩種可用目標:webandroid_app

Android 陳述式的 target 屬性包含下列欄位:

  • namespace:所有 Android 應用程式的 android_app
  • package_name:完整套件名稱 (com.devrel.deeplinksbasics)。
  • sha256_cert_fingerprints:應用程式憑證的指紋。下一節將說明如何產生這個憑證。

憑證指紋

有多種方法可取得憑證指紋。本程式碼研究室會採用兩種做法,一種用於應用程式偵錯版本,另一種則用於協助將應用程式發布到 Google Play 商店。

偵錯設定

Android Studio 首次執行專案時,會自動使用偵錯憑證簽署應用程式。這個憑證的位置是 $HOME/.android/debug.keystore。您可以使用 Gradle 指令取得這個 SHA-256 憑證指紋,步驟如下:

  1. 按兩下 Control,畫面上應該會顯示「Run anything」選單。如未顯示,可以在右側欄的 Gradle 選單中找到該選單,接著點選 Gradle 圖示。

已選取 Gradle 圖示的 Android Studio Gradle 選單分頁。

  1. 輸入 gradle signingReport,然後按下 Enter 鍵。這個指令會在控制台中執行,並顯示偵錯應用程式變化版本的指紋資訊。

「Terminal」視窗會顯示 Gradle 簽署報告結果。

  1. 如要完成網站關聯,請複製 SHA-256 憑證指紋、更新 JSON 檔案,然後將檔案上傳到位於 https://<domain>/.well-know/assetlinks.json 的網站。這篇「Android 應用程式連結」網誌文章可協助您進行設定。
  2. 如果應用程式仍在執行中,按下「Stop」即可停止應用程式。
  3. 如要重新啟動驗證程序,請將應用程式從模擬器中移除。在模擬器上點選並按住「DeepLinksBasics」應用程式圖示,然後選取「App Info」。在互動視窗中依序點選「Uninstall」和「Confirm」。接著執行應用程式,以便 Android Studio 驗證關聯。

f112e0d252c5eb48.gif

  1. 請確認您已選取「app」執行設定。否則 Gradle 簽署報告會再次執行。已選取「app」設定的 Android Studio 執行設定選單。
  2. 重新啟動應用程式,然後啟動含有 Android 應用程式連結網址的意圖:
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/"
  1. 請注意,應用程式會啟動,而意圖會顯示在主畫面上。

Android 模擬器主畫面,其中的文字欄位顯示成功實作的 Android 應用程式連結。

恭喜!您剛剛建立了第一個 Android 應用程式連結!

版本設定

現在,為了能夠將含有 Android 應用程式連結的應用程式上傳到 Play 商店,您必須使用包含合適憑證指紋的發布子版本。如要產生並上傳該版本,請按照下列步驟操作:

  1. 在 Android Studio 主選單中,依序按一下「Build」>「Generate Signed Bundle/APK」
  2. 在下一個對話方塊中,針對 Play 應用程式簽署功能選取「Android App Bundle」;如要直接部署至裝置,請選取「APK」
  3. 在下一個對話方塊中,按一下「Key store path」下方的「Create new」。系統會開啟新提示視窗。
  4. 為您的 KeyStore 選取路徑,然後將其命名為 basics-keystore.jks
  5. Keystore 建立密碼並輸入確認密碼。
  6. 將「Key」部分的「Alias」欄位保留預設值。
  7. 請確定輸入的密碼和確認密碼與 Keystore 中的密碼相同。兩者「必須」相符。
  8. 填寫「Certificate」資訊,然後按一下「OK」

Android Studio 的「New Key Store」互動視窗,其中含有以下的值和選單項目:「Key store path」的值為所選目錄、「Password」和「Confirm」的值為所選密碼、「Alias」的值為「key0」、「Password」和「Confirm」的值與首次輸入的密碼相同、「Validity」的值為預設值、「First and Last Name」的值為「Sabs sabs」、「Organizational Unit」的值為「Android」、「Organization」的值為「MyOrg」、「City or Locality」的值為「MyCity」、「State or Province」的值為「MyState」,「Country Code」的值為「US」。

  1. 確認已針對 Play 應用程式簽署功能勾選匯出加密金鑰的選項,然後點選「Next」

「Generate Sign Bundle or APK」選單互動視窗,其中包含以下的值和選單項目:「Module」的值為預設值、「Key store path」的值為系統產生的路徑、「Key store password」的值為先前產生的密碼、「Key alias」的值為「key0」、「Key password」的值為先前產生的密碼、勾選「Export encrypted key for enrolling published apps in Google Play App Signing」,「Encrypted key export path」的值為預設值。

  1. 在這個對話方塊中,選取發布版建構變數,然後按一下「Finish」。您現在可以將應用程式上傳至 Google Play 商店,並使用 Play 應用程式簽署功能。

Play 應用程式簽署

只要使用 Play 應用程式簽署功能,Google 就能協助您管理及保護應用程式的簽署金鑰。您只需上傳在先前步驟中完成的已簽署應用程式套件即可。

如要擷取 assetlinks.json 檔案的憑證指紋,並在發布的變化版本中提供 Android 應用程式連結,請按照下列步驟操作:

  1. 在 Google Play 管理中心按一下「Create app」
  2. 輸入「Deep Links Basics」做為應用程式名稱。
  3. 後續兩個選項請選取「App」和「Free」「Create app」選單,其中包含下列更新的值:「app name」的值為「Deep links basics」、「app or game」選項選取「App」、「free or paid」選項選取「free」,並且接受兩項聲明。
  4. 接受「聲明」,然後按一下「Create app」
  5. 如要上傳套件並測試 Android 應用程式連結,請在左選單中依序選取「Testing」>「internal testing」
  6. 按一下「Create new release」

Play 管理中心的「internal testing」專區,其中顯示「create new release」按鈕。

  1. 在下一個畫面中,按一下「Upload」,然後選取在上一節步驟中產生的套件。您可以依序前往「DeepLinksBascis」>「app」>「release」找到 app-release.aab 檔案。按一下「Open」,然後等待套件上傳完畢。
  2. 上傳後,請先將其餘欄位保留預設值。按一下「Save」

Play 管理中心的內部測試版本專區,其中含有已上傳的「deep links basics」應用程式。系統會自動填入預設值。

  1. 為了進行下一節的準備,請點選「Review release」,然後在下一個畫面中點選「Start rollout to Internal testing」。忽略警告,因為發布至 Play 商店不屬於本程式碼研究室的課程範圍。
  2. 在互動視窗中按一下「Rollout」
  3. 如要取得 Play 應用程式簽署功能建立的 SHA-256 憑證指紋,請前往左選單中的「Deep links」分頁,然後查看「深層連結」資訊主頁。

Play 管理中心的「Deep links」資訊主頁會顯示最近上傳的深層連結的所有相關資訊。

  1. 在「Domains」部分,按一下網站的網域。請注意,Google Play 管理中心提到您尚未驗證應用程式的網域 (網域關聯)。
  2. 在「Fix Domain Issues」部分下方,按一下「Show More」箭頭。
  3. 在這個畫面中,Google Play 管理中心說明如何更新包含憑證指紋的 assetlinks.json 檔案。複製程式碼片段並更新 assetlinks.json 檔案。

「Deep links」資訊主頁的網域驗證專區,說明如何更新含有正確憑證指紋的網域。

  1. 更新 assetlinks.json 檔案後,請按一下「Recheck verification」。如果尚未通過驗證,驗證服務最多需要五分鐘的時間,才會偵測到新的變更。
  2. 重新載入「Deep links」資訊主頁後,畫面就不會再顯示驗證錯誤。

驗證已上傳的應用程式

您已經瞭解如何驗證位於模擬器上的應用程式。現在,您要驗證已上傳至 Play 商店的應用程式。

如要在模擬器上安裝應用程式,並確保 Android 應用程式連結已通過驗證,請按照下列步驟操作:

  1. 按一下左側欄中的「Releases Overview」,然後選取您剛才上傳的最新版本,這個版本應為 1 (1.0) 版
  2. 按一下「Release details」(右側藍色箭頭),即可查看發布詳細資料。
  3. 按一下相同的藍色箭頭按鈕,即可取得應用程式套件資訊。
  4. 在這個互動視窗中,點選「Downloads」分頁標籤,然後在「Signed, universal APK」資產部分,按一下「download」圖示
  5. 將這個套件安裝至模擬器之前,請先刪除由 Android Studio 安裝的前一個應用程式。
  6. 在模擬器上點選並按住「DeepLinksBasics」應用程式圖示,然後選取「App Info」。在互動視窗中依序點選「Uninstall」和「Confirm」

f112e0d252c5eb48.gif

  1. 如要安裝已下載的套件,請將下載的 1.apk 檔案拖曳到模擬器畫面中,然後等待套件安裝完畢。

8967dac36ae545ee.gif

  1. 如要測試驗證程序,請在 Android Studio 中開啟終端機,並使用以下兩個指令執行驗證程序:
adb shell pm verify-app-links --re-verify com.devrel.deeplinksbasics
adb shell pm get-app-links com.devrel.deeplinksbasics
  1. 執行 get-app-links 指令之後,控制台中應該會顯示 verified 訊息。如果看到 legacy_failure 訊息,請確認憑證指紋與您為網站上傳的指紋相符。如果相符,但依然未顯示驗證訊息,請嘗試再次執行步驟 6、7 和 8。

控制台輸出內容。

7. 實作 Android 應用程式連結

現在您已完成所有設定,可以開始實作應用程式了。

系統將使用 Jetpack Compose 進行實作。如要進一步瞭解 Jetpack Compose,請參閱「使用 Jetpack Compose 加速建構更優質的應用程式」。

程式碼依附元件

如要加入及更新這項專案所需的幾個依附元件,請按照下列步驟操作:

  • 將以下內容加入 ModuleProject Gradle 檔案:

build.gradle (Project)

buildscript {
  ...
  dependencies {
    classpath "com.google.dagger:hilt-android-gradle-plugin:2.43"
  }
}

build.gradle (Module)

plugins {
  ...
  id 'kotlin-kapt'
  id 'dagger.hilt.android.plugin'
}
...
dependencies {
  ...
  implementation 'androidx.compose.material:material:1.2.1'
  ...
  implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
  implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
  implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
  implementation "com.google.dagger:hilt-android:2.43"
  kapt "com.google.dagger:hilt-compiler:2.43"
}

project zip 檔案中含有圖片目錄,其中有 10 張免權利金圖片可用於每間餐廳。您可以直接使用這些圖片,或加入自己的圖片。

如要新增 HiltAndroidApp 的主要進入點,請按照下列步驟操作:

  • 建立名為 DeepLinksBasicsApplication.kt 的新 Kotlin 類別/檔案,然後使用新的應用程式名稱更新資訊清單檔案。

DeepLinksBasicsApplication.kt

package com.devrel.deeplinksbasics

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class DeepLinksBasicsApplication : Application() {}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- Update name property -->
    <application
        android:name=".DeepLinksBasicsApplication"
        ...

資料

您需要為餐廳建立包含 Restaurant 類別、存放區和本機資料來源的資料層。所有內容都會保留在您必須建立的 data 套件中。如要進行這項作業,請按照以下步驟操作:

  1. Restaurant.kt 檔案中,使用下列程式碼片段建立 Restaurant 類別:

Restaurant.kt

package com.devrel.deeplinksbasics.data

import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable

@Immutable
data class Restaurant(
    val id: Int = -1,
    val name: String = "",
    val address: String = "",
    val type: String = "",
    val website: String = "",
    @DrawableRes val drawable: Int = -1
)
  1. RestaurantLocalDataSource.kt 檔案的資料來源類別中新增一些餐廳。請記得使用自己的網域更新資料。請參考下列程式碼片段:

RestaurantLocalDataSource.kt

package com.devrel.deeplinksbasics.data

import com.devrel.deeplinksbasics.R
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RestaurantLocalDataSource @Inject constructor() {
    val restaurantList = listOf(
        Restaurant(
            id = 1,
            name = "Pawtato",
            address = "3140 Skinner Hollow Road, Medford, Oregon 97501",
            type = "Potato and gnochi",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pawtato/",
            drawable = R.drawable.restaurant1,
        ),
        Restaurant(
            id = 2,
            name = "Rawrbucha",
            address = "2064 Carriage Lane, Mansfield, Ohio 44907",
            type = "Kombucha",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/rawrbucha/",
            drawable = R.drawable.restaurant2,
        ),
        Restaurant(
            id = 3,
            name = "Pizzabus",
            address = "1447 Davis Avenue, Petaluma, California 94952",
            type = "Pizza",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/pizzabus/",
            drawable = R.drawable.restaurant3,
        ),
        Restaurant(
            id = 4,
            name = "Keybabs",
            address = "3708 Pinnickinnick Street, Perth Amboy, New Jersey 08861",
            type = "Kebabs",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/keybabs/",
            drawable = R.drawable.restaurant4,
        ),
        Restaurant(
            id = 5,
            name = "BBQ",
            address = "998 Newton Street, Saint Cloud, Minnesota 56301",
            type = "BBQ",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/bbq/",
            drawable = R.drawable.restaurant5,
        ),
        Restaurant(
            id = 6,
            name = "Salades",
            address = "4522 Rockford Mountain Lane, Oshkosh, Wisconsin 54901",
            type = "salads",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/salades/",
            drawable = R.drawable.restaurant6,
        ),
        Restaurant(
            id = 7,
            name = "Gyros and moar",
            address = "1993 Bird Spring Lane, Houston, Texas 77077",
            type = "Gyro",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/gyrosAndMoar/",
            drawable = R.drawable.restaurant7,
        ),
        Restaurant(
            id = 8,
            name = "Peruvian ceviche",
            address = "2125 Deer Ridge Drive, Newark, New Jersey 07102",
            type = "seafood",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/peruvianCeviche/",
            drawable = R.drawable.restaurant8,
        ),
        Restaurant(
            id = 9,
            name = "Vegan burgers",
            address = "594 Warner Street, Casper, Wyoming 82601",
            type = "vegan",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/veganBurgers/",
            drawable = R.drawable.restaurant9,
        ),
        Restaurant(
            id = 10,
            name = "Taquitos",
            address = "1654 Hart Country Lane, Blue Ridge, Georgia 30513",
            type = "mexican",
            // TODO: Update with your own domain
            website = "https://your.own.domain/restaurants/taquitos/",
            drawable = R.drawable.restaurant10,
        ),
    )
}
  1. 記得將圖片匯入專案。
  2. 接著,在 RestaurantRepository.kt 檔案中加入 Restaurant 存放區,其中含有用來取得餐廳名稱的函式,如以下程式碼片段所示:

RestaurantRepository.kt

package com.devrel.deeplinksbasics.data

import javax.inject.Inject

class RestaurantRepository @Inject constructor(
    private val restaurantLocalDataSource: RestaurantLocalDataSource
){
    val restaurants: List<Restaurant> = restaurantLocalDataSource.restaurantList

    // Method to obtain a restaurant object by its name
    fun getRestaurantByName(name: String): Restaurant ? {
        return restaurantLocalDataSource.restaurantList.find {
            val processedName = it.name.filterNot { it.isWhitespace() }.lowercase()
            val nameToTest = name.filterNot { it.isWhitespace() }.lowercase()
            nameToTest == processedName
        }
    }
}

ViewModel

如要透過應用程式和 Android 應用程式連結選取餐廳,您必須建立 ViewModel 來變更所選餐廳的值。請按照以下步驟操作:

  • RestaurantViewModel.kt 檔案中,加入以下程式碼片段:

RestaurantViewModel.kt

package com.devrel.deeplinksbasics.ui.restaurant

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.devrel.deeplinksbasics.data.Restaurant
import com.devrel.deeplinksbasics.data.RestaurantRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class RestaurantViewModel @Inject constructor(
    private val restaurantRepository: RestaurantRepository,
) : ViewModel() {
    // restaurants and selected restaurant could be used as one UIState stream
    // which will scale better when exposing more data.
    // Since there are only these two, it is okay to expose them as separate streams
    val restaurants: List<Restaurant> = restaurantRepository.restaurants

    private val _selectedRestaurant = MutableStateFlow<Restaurant?>(value = null)
    val selectedRestaurant: StateFlow<Restaurant?>
        get() = _selectedRestaurant

    // Method to update the current restaurant selection
    fun updateSelectedRestaurantByName(name: String) {
        viewModelScope.launch {
            val selectedRestaurant: Restaurant? = restaurantRepository.getRestaurantByName(name)
            if (selectedRestaurant != null) {
                _selectedRestaurant.value = selectedRestaurant
            }
        }
    }
}

Compose

現在,您已擁有 ViewModel 和資料層的邏輯,接下來要新增 UI 層。多虧了 Jetpack Compose 程式庫,您只需要幾個步驟就能完成。就這個應用程式而言,您希望以格狀資訊卡方式顯示餐廳。使用者只要按一下每張資訊卡,即可查看各餐廳的詳細資料。您需要三個主要的可組合函式,以及一個指向對應餐廳路徑的導覽元件。

顯示餐廳應用程式成品的 Android 模擬器。

如要新增 UI 層,請按照下列步驟操作:

  1. 從可顯示各餐廳詳細資料的可組合函式開始。在 RestaurantCardDetails.kt 檔案中,加入以下程式碼片段:

RestaurantCardDetails.kt

package com.devrel.deeplinksbasics.ui

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCardDetails (
    restaurant: Restaurant,
    onBack: () -> Unit,
) {
    BackHandler() {
       onBack()
    }
    Scaffold(
        topBar = {
            TopAppBar(
                backgroundColor = Color.Transparent,
                elevation = 0.dp,
            ) {
                Row(
                    horizontalArrangement = Arrangement.Start,
                    modifier = Modifier.padding(start = 8.dp)
                ) {
                    Icon(
                        imageVector = Icons.Default.ArrowBack,
                        contentDescription = "Arrow Back",
                       modifier = Modifier.clickable {
                            onBack()
                        }
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text(text = restaurant.name)
                }
            }
        }
    ) { paddingValues ->
        Card(
            modifier = Modifier
                .padding(paddingValues)
                .fillMaxWidth(),
            elevation = 2.dp,
            shape = RoundedCornerShape(corner = CornerSize(8.dp))
        ) {
            Column(
                modifier = Modifier
                    .padding(16.dp)
                    .fillMaxWidth()
            ) {
                Text(text = restaurant.name, style = MaterialTheme.typography.h6)
                Text(text = restaurant.type, style = MaterialTheme.typography.caption)
                Text(text = restaurant.address, style = MaterialTheme.typography.caption)
                SelectionContainer {
                    Text(text = restaurant.website, style = MaterialTheme.typography.caption)
                }
                Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
            }
        }
    }
}
  1. 接下來,實作網格和格線本身。在 RastaurantCell.kt 檔案中,加入以下程式碼片段:

RestaurantCell.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantCell(
    restaurant: Restaurant
){
    Card(
        modifier = Modifier
            .padding(horizontal = 8.dp, vertical = 8.dp)
            .fillMaxWidth(),
        elevation = 2.dp,
        shape = RoundedCornerShape(corner = CornerSize(8.dp))
    ) {
        Column(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text(text = restaurant.name, style = MaterialTheme.typography.h6)
            Text(text = restaurant.address, style = MaterialTheme.typography.caption)
            Image(painter = painterResource(id = restaurant.drawable), contentDescription = "${restaurant.name}")
        }
    }
}
  1. RestaurantGrid.kt 檔案中,加入以下程式碼片段:

RestaurantGrid.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.devrel.deeplinksbasics.data.Restaurant

@Composable
fun RestaurantGrid(
    restaurants: List<Restaurant>,
    onRestaurantSelected: (String) -> Unit,
    navigateToRestaurant: (String) -> Unit,
) {
    Scaffold(topBar = {
        TopAppBar(
            backgroundColor = Color.Transparent,
            elevation = 0.dp,
        ) {
            Text(text = "Restaurants", fontWeight = FontWeight.Bold)
        }
    }) { paddingValues ->
        LazyVerticalGrid(
            columns = GridCells.Adaptive(minSize = 200.dp),
            modifier = Modifier.padding(paddingValues)
        ) {
            items(items = restaurants) { restaurant ->
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable(onClick = {
                            onRestaurantSelected(restaurant.name)
                            navigateToRestaurant(restaurant.name)
                        })
                ) {
                    RestaurantCell(restaurant)
                }
            }
        }
    }
}
  1. 接下來,您需要實作應用程式狀態和導覽邏輯,並更新 MainActivity.kt。使用者只要點選餐廳資訊卡,系統就會將他們導向特定餐廳。在 RestaurantAppState.kt 檔案中,加入以下程式碼片段:

RestaurantAppState.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController

sealed class Screen(val route: String) {
   object Grid : Screen("restaurants")
   object Name : Screen("restaurants/{name}") {
       fun createRoute(name: String) = "restaurants/$name"
   }
}

@Composable
fun rememberRestaurantAppState(
    navController: NavHostController = rememberNavController(),
) = remember(navController) {
    RestaurantAppState(navController)
}

class RestaurantAppState(
    val navController: NavHostController,
) {
    fun navigateToRestaurant(restaurantName: String) {
        navController.navigate(Screen.Name.createRoute(restaurantName))
    }

    fun navigateBack() {
        navController.popBackStack()
    }
}
  1. 關於導覽,您必須建立 NavHost,並使用可組合路徑導向各餐廳。在 RestaurantApp.kt 檔案中,加入以下程式碼片段:

RestaurantApp.kt

package com.devrel.deeplinksbasics.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.devrel.deeplinksbasics.ui.restaurant.RestaurantViewModel

@Composable
fun RestaurantApp(
   viewModel: RestaurantViewModel = viewModel(),
   appState: RestaurantAppState = rememberRestaurantAppState(),
) {
    val selectedRestaurant by viewModel.selectedRestaurant.collectAsState()
    val onRestaurantSelected: (String) -> Unit = { viewModel.updateSelectedRestaurantByName(it) }

    NavHost(
        navController = appState.navController,
        startDestination = Screen.Grid.route,
    ) {
        // Default route that points to the restaurant grid
        composable(Screen.Grid.route) {
            RestaurantGrid(
                restaurants = viewModel.restaurants,
                onRestaurantSelected = onRestaurantSelected,
                navigateToRestaurant = { restaurantName ->
                    appState.navigateToRestaurant(restaurantName)
                },
            )
        }
        // Route for the navigation to a particular restaurant when a user clicks on it
        composable(Screen.Name.route) {
            RestaurantCardDetails(restaurant = selectedRestaurant!!, onBack = appState::navigateBack)
        }
    }
}
  1. 您現在可以透過應用程式例項更新 MainActivity.kt 了。使用下列程式碼取代檔案:

MainActivity .kt

package com.devrel.deeplinksbasics

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.ui.Modifier
import com.devrel.deeplinksbasics.ui.RestaurantApp
import com.devrel.deeplinksbasics.ui.theme.DeepLinksBasicsTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DeepLinksBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    RestaurantApp()
                }
            }
        }
    }
}
  1. 執行應用程式,前往格狀檢視畫面並選取特定餐廳。選取餐廳後,應用程式會顯示該餐廳及其詳細資訊。

fecffce863113fd5.gif

現在,請將 Android 應用程式連結新增至格狀檢視畫面和每間餐廳。您已經為 /restaurants 中的格狀檢視畫面設定 AndroidManifest.xml 部分。最有效率的做法是針對每間餐廳使用相同設定,這樣的話,只需要在邏輯中新增路徑設定即可。如要進行這項作業,請按照以下步驟操作:

  1. 使用意圖篩選器更新資訊清單檔案,以便接收 /restaurants 做為路徑。請記得將您的網域新增為主機。在 AndroidManifest.xml 檔案中,加入以下程式碼片段:

AndroidManifest.xml

...
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:scheme="http"/>
  <data android:scheme="https"/>
  <data android:host="your.own.domain"/>
  <data android:pathPrefix="/restaurants"/>
</intent-filter>
  1. RestaurantApp.kt 檔案中,加入以下程式碼片段:

RestaurantApp.kt

...
import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.navigation.navDeepLink

fun RestaurantApp(...){
  NavHost(...){
    ...
    //  Route for the navigation to a particular restaurant when a user clicks on it
    //  and for an incoming deep link
    // Update with your own domain
        composable(Screen.Name.route,
            deepLinks = listOf(
                navDeepLink { uriPattern = "https://your.own.domain/restaurants/{name}" }
            ),
            arguments = listOf(
                navArgument("name") {
                    type = NavType.StringType
                }
            )
        ) { entry ->
            val restaurantName = entry.arguments?.getString("name")
            if (restaurantName != null) {
                LaunchedEffect(restaurantName) {
                    viewModel.updateSelectedRestaurantByName(restaurantName)
                }
            }
            selectedRestaurant?.let {
                RestaurantCardDetails(
                    restaurant = it,
                    onBack = appState::navigateBack
                )
            }
        }
  }
}

基本上,NavHost 會將 Android 意圖 Uri 資料與可組合路徑進行比對。如果路徑相符,系統會算繪 composable

composable 元件可使用 deepLinks 參數,其中包含來自意圖篩選器的 URI 清單。在本程式碼研究室中,您將新增已建立網站的網址,並定義 ID 參數,藉此接收使用者並將其傳送至特定餐廳。

  1. 為確保當使用者點選 Android 應用程式連結後,應用程式邏輯會將他們傳送至對應餐廳,請使用 adb
adb shell am start -W -a android.intent.action.VIEW -d "https://sabs-deeplinks-test.web.app/restaurants/gyrosAndMoar"

請注意,應用程式會顯示對應的餐廳。

顯示「gyros and moar」餐廳畫面的 Android 模擬器中的餐廳應用程式。

8. 查看 Play 管理中心資訊主頁

您已經看過深層連結的資訊主頁。這個資訊主頁會提供所有確保深層連結正常運作的必要資訊,甚至還能查看每個應用程式版本!這個頁面會顯示已在資訊清單檔案中新增的網域、連結和自訂連結。如果 assetlinks.json 檔案發生問題,此頁面還會顯示用來更新檔案的位置。

Play 管理中心的「深層連結」資訊主頁,有一個 Android 應用程式連結已通過驗證。

9. 結論

恭喜,您已成功建構第一個 Android 應用程式連結應用程式!

您已瞭解設計、設定、建立及測試 Android 應用程式連結的程序。這個程序有許多不同部分,因此本程式碼研究室匯總了所有詳細資料,協助您順利進行 Android OS 開發作業。

您現已瞭解處理 Android 應用程式連結的重要步驟。

其他資訊

參考說明文件