【问题标题】:Navigating to a composable using a deeplink with Jetpack Compose使用 Jetpack Compose 的深度链接导航到可组合项
【发布时间】:2022-06-27 17:31:24
【问题描述】:

当用户在我们的应用程序中输入地理围栏时,我们会向他们显示有关该区域的报价通知,当点击该通知时,应将他们引导至名为 SingleNotification 的特定可组合屏幕。我已经关注了谷歌的codelab 和他们的documentation,但我还没有设法使导航到特定屏幕工作。现在,点击通知或运行adb shell am start -d “eway://station_offers/date_str/www.test.com/TITLE/CONTENT” -a android.intent.action.VIEW 命令,即可打开应用程序。

活动在清单中声明如下:

    <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 类包含 NavHost,而 NavHost 又包含各种 NavGraph。我只包括了下面的相关图表:

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

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)

我无法弄清楚我在这里缺少什么。

【问题讨论】:

    标签: android kotlin android-jetpack-compose android-deep-link


    【解决方案1】:

    好的,经过大量的测试,并逐行运行了谷歌相关代码实验室的解决方案,我想出了如何让它工作。 首先,看起来我们在AndroidManifest.xml 中为意图过滤器的&lt;data&gt; 标签定义的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>
    

    其次,深层链接的 uri 模式应该与可组合的路由格式相匹配。在这种情况下,由于可组合的路由定义为route = "${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}",正确的深层链接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. 深层链接的可组合目标必须是主 NavHost 的直接子节点
    2. AndroidManifest 的 android:host 应该匹配目标可组合的路由,最后,
    3. 深层链接的 Uri 模式应与目标可组合的路由匹配(如果您使用格式 scheme://host/....,如果您遵循数字 2 应该没问题)

    【讨论】:

      【解决方案2】:

      事实证明,this answer 中描述的限制并不完全正确。具体来说,

      1. 可以将通知中的深层链接直接链接到嵌套图中的目标位置
      2. 目的地的路由与 deepLink URI 之间没有关系。

      上面的第 2 点是解开我对深度链接工作原理的理解的关键。它们只是任意的 URI,与目的地的路由完全没有关系。规则是以下3项必须匹配

      1. 在可组合的 navDeepLink DSL 中定义的 URI 模式
      2. 用于构造通知的 PendingIntent 的 URI
      3. 在清单的intent-filter 中声明的schemehost

      这里有一些代码 sn-ps。在我的情况下,URI 是静态的,因此您需要进行调整以解决 OP 的情况。这个例子有如下结构

      • LandingScreen ("landing_screen_route")
      • SecondScreen ("second_screen_route")
      • 嵌套图 ("nested_graph_route") 和 NestedScreen ("nested_destination_route")

      我们将了解如何通过通知同时访问SecondScreenNestedScreen

      首先,使用 DSL 定义 NavGraph。请特别注意此处的navDeepLink 条目。

      @Composable
      fun AppGraph(onNotifyClick: () -> Unit) {
          val navController = rememberNavController()
          NavHost(
              navController = navController,
              startDestination = "landing_screen_route"
          ) {
              composable("landing_screen_route") {
                  LandingScreen {
                      navController.navigate("second_screen_route")
                  }
              }
              composable(
                  route = "second_screen_route",
                  deepLinks = listOf(
                      navDeepLink { uriPattern = "myapp://arbitrary_top_level" } // Note that this pattern has no relation to the route itself
                  )
              ) {
                  SecondScreen {
                      navController.navigate("nested_graph_route")
                  }
              }
              navigation(
                  startDestination = "nested_destination_route",
                  route = "nested_graph_route"
              ) {
                  composable(
                      route = "nested_destination_route",
                      deepLinks = listOf(
                          navDeepLink { uriPattern = "myapp://arbitrary_nested" } // Note that this pattern has no relation to the route itself
                      )
                  ) {
                      NestedScreen(onNotifyClick)
                  }
              }
          }
      }
      
      

      接下来,您将为这两种情况构建 PendingIntent:

      val notNestedIntent = TaskStackBuilder.create(this).run {
          addNextIntentWithParentStack(
              Intent(
                  Intent.ACTION_VIEW,
                  "myapp://arbitrary_top_level".toUri() // <-- Notice this
              )
          )
          getPendingIntent(1234, PendingIntent.FLAG_UPDATE_CURRENT)
      }
      
      val nestedIntent = TaskStackBuilder.create(this).run {
          addNextIntentWithParentStack(
              Intent(
                  Intent.ACTION_VIEW,
                  "myapp://arbitrary_nested".toUri() // <-- Notice this
              )
          )
          getPendingIntent(2345, PendingIntent.FLAG_UPDATE_CURRENT)
      }
      

      最后,这是清单中的intent-filter 条目

      <activity
          android:name=".MainActivity">
          <intent-filter>
              <action android:name="android.intent.action.VIEW" />
              <category android:name="android.intent.category.DEFAULT" />
      
              <!--
                  The scheme and host must match both of the below:
                  1. The navDeepLink declaration
                  2. The URI defined in the PendingIntent
               -->
              <data
                  android:scheme="myapp"
                  android:host="arbitrary_top_level"
              />
          </intent-filter>
          <intent-filter>
              <action android:name="android.intent.action.VIEW" />
              <category android:name="android.intent.category.DEFAULT" />
      
              <!--
                  The scheme and host must match both of the below:
                  1. The navDeepLink declaration
                  2. The URI defined in the PendingIntent
               -->
              <data
                  android:scheme="myapp"
                  android:host="arbitrary_nested"
              />
          </intent-filter>
      </activity>
      

      【讨论】:

        猜你喜欢
        • 2021-12-15
        • 1970-01-01
        • 2021-12-22
        • 2022-08-10
        • 1970-01-01
        • 2023-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多