使用 Jetpack Compose 的深层链接导航到可组合项

Navigating to a composable using a deeplink with Jetpack Compose

当用户在我们的应用程序中输入地理围栏时,我们会向他们显示有关该区域的优惠通知,单击该通知后,应将他们引导至名为 SingleNotification 的特定可组合屏幕。我已经关注了 google 的 codelab and their documentation,但我还没有成功地导航到特定的屏幕。现在,单击通知或 运行 adb shell am start -d “eway://station_offers/date_str/www.test.com/TITLE/CONTENT” -a android.intent.action.VIEW 命令,只需打开应用程序。

activity在清单中声明如下:

    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:label="@string/app_name"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.intent.action.VIEW" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />

            <data
                android:host="station_offers"
                android:scheme="eway" />
        </intent-filter>
    </activity>

我们的 MainNavController class 包含 NavHost,后者又包含各种 NavGraph。我只包含了下面的相关图表:

        NavHost(
            navController = navController,
            startDestination = NavigationGraphs.SPLASH_SCREEN.route
        ) {
....
            notificationsNavigation()
....    
    }

通知导航图定义如下:

fun NavGraphBuilder.notificationsNavigation() {
    navigation(
        startDestination = Screens.NOTIFICATION_DETAILS.navRoute,
        route = NavigationGraphs.NOTIFICATIONS.route
    ) {
        composable(
            route = "${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}",
            arguments = listOf(
                navArgument("date") { type = NavType.StringType },
                navArgument("imageUrl") { type = NavType.StringType },
                navArgument("title") { type = NavType.StringType },
                navArgument("content") { type = NavType.StringType }
            ),
            deepLinks = listOf(navDeepLink {
                uriPattern = "eway://${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}"
            })
        ) { backstackEntry ->
            val args = backstackEntry.arguments
            SingleNotification(
                date = args?.getString("date")!!,
                imageUrl = args.getString("imageUrl")!!,
                title = args.getString("title")!!,
                description = args.getString("content")!!
            )
        }
    }
}

Screes.NOTIFICATION_DETAILS.navRoute对应notification_details的值。

在地理围栏广播接收器中,我构造了如下的未决Intent:

                        val deepLinkIntent = Intent(
                            Intent.ACTION_VIEW,
                            "eway://station_offers/${
                                offer.date
                            }/${
                                offer.image
                            }/${offer.title}/${offer.content}".toUri(),
                            context,
                            MainActivity::class.java
                        )
                        val deepLinkPendingIntent: PendingIntent =
                            TaskStackBuilder.create(context!!).run {
                                addNextIntentWithParentStack(deepLinkIntent)
                                getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)!!
                            }
                        showNotification(offer.title, offer.content, deepLinkPendingIntent)

我不知道我在这里遗漏了什么。

好吧,经过大量测试和 运行 Google 的相关代码实验室的解决方案,一行一行地进行了多次测试,我想出了如何让它工作的方法。 首先,看起来我们在 AndroidManifest.xml 中为 intent 过滤器的 <data> 标签定义的 host 需要很多可组合目的地的路由。所以在我的例子中,它被定义为:

        <intent-filter>
            <action android:name="android.intent.action.VIEW" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />

            <data
                android:host="notification_details"
                android:scheme="eway" />
        </intent-filter>

其次,深度 link 的 uri 模式应与可组合项的路由格式相匹配。在这种情况下,由于可组合项的路由定义为 route = "${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}",正确的深度 Link uriPattern 将是:

deepLinks = listOf(navDeepLink {
                    uriPattern =
                        "eway://${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}"
                })

此外,可组合目的地似乎必须在 NavHost 本身内声明,而不是在 NavGraph 内声明。最初如你所见,我认为系统可以通过嵌套的 NavGraph 找到目的地,但它不能(抛出一个相对的异常),所以我得出的结论是它必须以这种方式完成(如在代码实验室中完成)。如有错误请指正!

最后,我相应地更改了 GeofenceBroadcastReceiver 中的 val uri 定义。现在看起来像这样:

val uri = "eway://${Screens.NOTIFICATION_DETAILS.navRoute}/${
                                    offer.date.replace(
                                        "/",
                                        "@"
                                    )
                                }/${
                                    offer.image.replace(
                                        "/",
                                        "@"
                                    )
                                }/${offer.title}/${offer.content.replace("/", "@")}".toUri()

总结一下,据我所知,这些步骤似乎可以解决这个问题:

  1. 深层 link 的目标可组合项必须是主 NavHost
  2. 的直接子项
  3. AndroidManifest 的 android:host 应该匹配目标可组合项的路由,最后,
  4. 深层 link 的 URI 模式应与目标可组合项的路由相匹配(如果您使用 scheme://host/.... 格式,则遵循数字 2 应该没问题)