建立指向特定目的地的深層連結

在 Android 中,深層連結是可讓您直接連往應用程式的特定目的地的連結。

您可以在應用程式中支援兩種不同的深層連結:明確隱含。導入深層連結的方式因應用程式使用的圖表類型 (XML or programmatic) 而異。

建立明確深層連結

明確深層連結是深層連結的一種單獨執行個體,會使用 PendingIntent 將使用者帶往應用程式中的特定位置。舉例來說,您可以在通知或應用程式小工具中顯示明確深層連結。

當使用者透過明確深層連結開啟應用程式時,系統會清除工作返回堆疊,並替換為深層連結的目的地。在建立巢狀結構圖時,每個巢狀結構層級的起始目的地也會新增至堆疊中(即階層中每個 <navigation> 元素的開始目的地)。也就是說,當使用者按下深層連結目的地中的「Back」按鈕時,就會返回導覽堆疊,如同從進入點進入應用程式時一樣。

程式輔助圖表

如果導覽圖是以程式輔助方式定義 (Navigation Compose 或 Kotlin DSL 的常見做法),建議使用 TaskStackBuilder 建立深層連結 PendingIntent

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/profile/$id".toUri(),
    context,
    MyActivity::class.java
)

val pendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

XML 圖表

您可以使用 NavDeepLinkBuilder 類別建構 PendingIntent,如下方範例所示。請注意,如果提供的內容並非 Activity,建構函式則會使用 PackageManager.getLaunchIntentForPackage() (如果有) 做為預設的啟動活動。

Kotlin

val pendingIntent = NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.android)
    .setArguments(args)
    .createPendingIntent()

Java

PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.android)
    .setArguments(args)
    .createPendingIntent();

根據預設,NavDeepLinkBuilder 會以應用程式資訊清單中宣告的預設啟動 Activity 來啟動明確深層連結。如果 NavHost 處於其他的活動中,則必須在建立深層連結建構工具時指定其元件名稱:

Kotlin

val pendingIntent = NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.android)
    .setArguments(args)
    .setComponentName(DestinationActivity::class.java)
    .createPendingIntent()

Java

PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
        .setGraph(R.navigation.nav_graph)
        .setDestination(R.id.android)
        .setArguments(args)
        .setComponentName(DestinationActivity.class)
        .createPendingIntent();

如果已有 ComponentName,可以直接將其傳送至建構工具:

Kotlin

val componentName = ...

val pendingIntent = NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.android)
    .setArguments(args)
    .setComponentName(componentName)
    .createPendingIntent()

Java

ComponentName componentName = ...;

PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
        .setGraph(R.navigation.nav_graph)
        .setDestination(R.id.android)
        .setArguments(args)
        .setComponentName(componentName)
        .createPendingIntent();

如果您已有 NavController,也可以使用 NavController.createDeepLink() 建立深層連結。

建立隱含深層連結

隱含深層連結是指應用程式中特定的目的地。在叫用深層連結時 (例如當使用者點擊連結時),Android 就可以將應用程式開啟至對應的目的地。

深層連結可依 URI、意圖動作及 MIME 類型進行比對符合。可以為單一的深層連結指定多個比對類型,但請注意,系統會優先比對 URI 引數,接著是操作,然後是 MIME 類型。

程式輔助圖表

如果您以程式輔助方式定義導覽圖 (使用 Navigation Compose 或 Kotlin DSL),則可在程式碼中定義深層連結。

撰寫

在 Navigation Compose 中,您可以使用 deepLinks 參數,將深層連結定義為 composable() 目的地建構工具的一部分。它接受 NavDeepLink 物件清單,您可以使用 navDeepLink() 函式建立:

@Serializable
data class Profile(val id: String)

val uri = "https://www.example.com"

composable<Profile>(
  deepLinks = listOf(
    navDeepLink<Profile>(basePath = "$uri/profile")
  )
) { backStackEntry ->
  val profile: Profile = backStackEntry.toRoute()
  ProfileScreen(id = profile.id)
}

Kotlin DSL

使用 Kotlin DSL 時,您可以在目的地區塊中,使用 deepLink() 建構工具函式定義深層連結:

@Serializable
data class Profile(val id: String)

val uri = "https://www.example.com"

fragment<ProfileFragment, Profile> {
    deepLink<Profile>(basePath = "$uri/profile")
}

為程式輔助圖表新增意圖篩選器

由於程式輔助導覽圖是在執行階段建構,因此 Navigation 元件無法在 AndroidManifest.xml 中自動產生相符的 <intent-filter> 元素。您必須改為手動新增適當的 <intent-filter> 元素。

如要啟用上述範例中的深層連結,請在資訊清單中對應的 <activity> 元素內新增以下內容:

<activity …>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

XML 圖表

如要在以 XML 為基礎的圖表中建立隱含深層連結,可以直接在 XML 中定義 <deepLink> 元素,也可以使用 Navigation 編輯器。

以下是含有 URI、操作及 MIME 類型的深層連結範例:

<fragment android:id="@+id/a"
          android:name="com.example.myapplication.FragmentA"
          tools:layout="@layout/a">
        <deepLink app:uri="www.example.com"
                app:action="android.intent.action.MY_ACTION"
                app:mimeType="type/subtype"/>
</fragment>

您也可以使用瀏覽編輯器建立前往目的地的隱含深層連結,如下所示:

  1. 在導覽編輯器的「Design」分頁中,選取深層連結的目的地。
  2. 在「Attributes」面板的「Deep Links」部分中按一下「+」
  3. 在隨即出現的「Add Deep Link」對話方塊中,輸入深層連結的相關資訊。

    注意事項:

    • 系統會將無配置的 URI 視為 httphttps。例如,www.google.com 可符合 http://www.google.comhttps://www.google.com 兩者。
    • {placeholder_name} 格式的路徑參數預留位置符合一個或多個字元。例如,http://www.example.com/users/{id} 符合 http://www.example.com/users/4。瀏覽元件會嘗試比對預留位置名稱與定義的引數(針對深層連結目的地所定義),從而將預留位置的值剖析為適當的類型。如果不具有定義相同名稱的引數,引數值會使用預設的 String 類型。可以使用 .* 萬用字元來比對 0 個以上的字元。
    • 可以使用查詢參數預留位置來取代路徑參數,也可以搭配路徑參數使用。例如,http://www.example.com/users/{id}?myarg={myarg} 符合 http://www.example.com/users/4?myarg=28
    • 使用預設值或空值定義的查詢變數參數預留位置則不需進行比對。舉例來說,http://www.example.com/users/{id}?arg1={arg1}&arg2={arg2}http://www.example.com/users/4?arg2=28http://www.example.com/users/4?arg1=7 相符。但具有路徑參數則否。舉例來說,http://www.example.com/users?arg1=7&arg2=28 並不符合上述模式,因為未提供必要路徑參數。
    • 多餘的查詢參數不會影響深層連結 URI 比對。舉例來說,儘管 URI 模式中並未定義 extraneousParamhttp://www.example.com/users/{id} 也能與 http://www.example.com/users/4?extraneousParam=7 相符。
  4. (選用) 勾選「Auto Verify」,要求 Google 驗證您是 URI 的擁有者。詳情請參閱「驗證 Android 應用程式連結」。

  5. 按一下「Add」。連結圖示 會顯示在所選目的地上方,表示該目的地設有深層連結。

  6. 按一下「Code」分頁標籤,切換至 XML 檢視畫面。具有巢狀結構的 <deepLink> 元素已新增至目的地:

    <deepLink app:uri="https://www.google.com" />
    

如要為以 XML 為基礎的圖表啟用隱含深層連結,還必須在應用程式的 manifest.xml 檔案中新增內容。將單一 <nav-graph> 元素新增至指向現有瀏覽圖的活動,如以下範例所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <application ... >

        <activity name=".MainActivity" ...>
            ...

            <nav-graph android:value="@navigation/nav_graph" />

            ...

        </activity>
    </application>
</manifest>

建構專案時,Navigation 元件會將 <nav-graph> 元素替換成產生的 <intent-filter> 元素,以符合導覽圖中的所有深層連結。

隱含深層連結和返回堆疊

觸發隱含深層連結時,返回堆疊的狀態取決於隱含式 Intent 是否透過 Intent.FLAG_ACTIVITY_NEW_TASK 旗標啟動:

  • 如果設定了旗標,系統會清除工作返回堆疊,並以深層連結目的地取代。與明確深層連結一樣,在為圖表建立巢狀結構時,每個巢狀結構層級的起始目的地 (即階層中每個 <navigation> 元素的起始目的地) 也會新增至堆疊中。也就是說,當使用者按下深層連結目的地中的「返回按鈕」時,就會瀏覽回返回堆疊,就像從進入點進入應用程式一樣。
  • 如未設定旗標,此時仍會停留在上個應用程式的工作堆疊中,這是觸發隱含深層連結的位置。在此情況下,「Back」按鈕會將使用者帶回到上一個應用程式,而「Up」按鈕則會在導覽圖的階層父項目的地上啟動應用程式的工作。

處理深層連結

使用瀏覽時,強烈建議您一律使用 standard 的預設 launchMode。使用 standard 啟動模式時,瀏覽功能會自動處理深層連結,方法是呼叫 handleDeepLink(),藉此處理 Intent 內的任何明確或隱含深層連結。不過,如果在使用替代的 launchMode (例如 singleTop) 時重新使用了 Activity,系統並不會自動執行這項操作。在此情況下,必須在 onNewIntent() 中手動呼叫 handleDeepLink(),如下例所示:

Kotlin

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    navController.handleDeepLink(intent)
}

Java

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    navController.handleDeepLink(intent);
}