デスティネーションへのディープリンクを作成する

Android では、アプリ内の特定のデスティネーションにユーザーを直接誘導するリンクをディープリンクと呼びます。

アプリでは、「明示的」と「暗黙的」という 2 種類のディープリンクをサポートできます。ディープリンクの実装方法は、アプリで使用するグラフの種類(XML or programmatic)によって異なります。

明示的ディープリンクを作成する

明示的ディープリンクは、PendingIntent を使用してアプリ内の特定の場所にユーザーを誘導するディープリンクの単一インスタンスです。たとえば、通知やアプリ ウィジェットの一部として明示的ディープリンクを表示できます。

ユーザーが明示的ディープリンクを通じてアプリを開くと、タスク バックスタックがクリアされ、ディープリンク デスティネーションに置き換えられます。グラフをネストしている場合は、各ネストレベルの開始デスティネーション(つまり、階層内の各 <navigation> 要素の開始デスティネーション)もスタックに追加されます。そのため、ユーザーがディープリンク デスティネーションから [戻る] ボタンを押すと、アプリにそのエントリ ポイントからアクセスしたかのようにナビゲーション スタックを戻ることができます。

プログラムによるグラフ

ナビゲーション グラフがプログラムで定義されている場合(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 Editor を使用します。

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>

Navigation Editor を使用して、デスティネーションへの暗黙的ディープリンクを作成することもできます。手順は次のとおりです。

  1. Navigation Editor の [Design] タブで、ディープリンクのデスティネーションを選択します。
  2. [Attributes] パネルの [Deep Links] セクションで、[+] をクリックします。
  3. 表示された [Add Deep Link] ダイアログで、ディープリンクの情報を入力します。

    次の点にご注意ください。

    • スキーマのない URI は、http または https のいずれかと見なされます。たとえば、www.google.comhttp://www.google.comhttps://www.google.com の両方に一致します。
    • {placeholder_name} 形式のパスパラメータ プレースホルダは、1 つ以上の文字に一致します。たとえば、http://www.example.com/users/{id}http://www.example.com/users/4 に一致します。Navigation コンポーネントは、プレースホルダ名を、ディープリンク デスティネーション用に定義された引数とマッチングすることにより、プレースホルダ値を解析して適切な型に解決しようと試みます。同じ名前を持つ引数が定義されていない場合は、デフォルトの String 型が引数の値に使用されます。ワイルドカード「.*」を使用すると、0 個以上の文字に一致させることができます。
    • クエリ パラメータ プレースホルダは、パスパラメータの代わりに使用することも、パスパラメータと一緒に使用することもできます。たとえば、http://www.example.com/users/{id}?myarg={myarg}http://www.example.com/users/4?myarg=28 に一致します。
    • デフォルト値または null 許容値で定義された変数のクエリ パラメータ プレースホルダは、マッチングが不要です。たとえば、http://www.example.com/users/{id}?arg1={arg1}&arg2={arg2}http://www.example.com/users/4?arg2=28 または http://www.example.com/users/4?arg1=7 と一致します。この点がパスパラメータと異なります。たとえば、http://www.example.com/users?arg1=7&arg2=28 は、必須のパスパラメータが指定されていないため、上記のパターンに一致しません。
    • 追加のクエリ パラメータは、ディープリンク URI のマッチングには影響しません。たとえば、extraneousParam が URI パターンで定義されていなくても、http://www.example.com/users/{id}http://www.example.com/users/4?extraneousParam=7 に一致します。
  4. (省略可)自分が URI のオーナーであることが必ず Google によって検証されるようにするには、[Auto Verify] チェックボックスをオンにします。詳しくは、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> 要素に置き換えて、ナビゲーション グラフ内のすべてのディープリンクに一致するようにします。

暗黙的ディープリンクとバックスタック

暗黙的ディープリンクをトリガーする場合、暗黙的 IntentIntent.FLAG_ACTIVITY_NEW_TASK フラグ付きで起動したかどうかによって、バックスタックの状態が変わります。

  • フラグがセットされている場合、タスク バックスタックはクリアされ、ディープリンク デスティネーションに置き換えられます。明示的ディープリンクと同様、グラフをネストしている場合は、各ネストレベルの開始デスティネーション(つまり、階層内の各 <navigation> 要素の開始デスティネーション)もスタックに追加されます。そのため、ユーザーがディープリンク デスティネーションから [戻る] ボタンを押すと、アプリにそのエントリ ポイントからアクセスしたかのようにナビゲーション スタックを戻ることができます。
  • フラグがセットされていない場合、ユーザーは、暗黙的ディープリンクをトリガーした元のアプリのタスクスタック上にとどまります。この場合、[戻る] ボタンを押すと前のアプリに戻り、[上へ] ボタンを押すと、ナビゲーション グラフ階層内の親デスティネーション上にあるアプリのタスクが開始されます。

ディープリンクを処理する

Navigation を使用する際は、常にデフォルトの standard launchMode を使用することを強くおすすめします。standard 起動モードを使用すると、Navigation は Intent 内の明示的・暗黙的ディープリンクを処理する handleDeepLink() を呼び出して、ディープリンクを自動的に処理します。しかし、singleTop など別の launchMode を使用すると、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);
}