带有深层链接的Android导航组件:多次调用onNewIntent

时间:2020-05-22 09:28:14

标签: android android-jetpack android-architecture-navigation android-jetpack-navigation

这一次,我需要您与使用带有深层链接的android导航组件有关的帮助。

我一直在关注documentation,并且fragment和Deeplink之间的连接正常。

问题在于接收深度链接的活动。就我而言,我设置了android:launchMode =“ singleTask”

<activity android:name=".features.welcome.WelcomeActivity"
    android:launchMode="singleTask">
     <nav-graph android:value="@navigation/welcome_nav_graph" />
</activity>

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    Timber.d("onNewIntent: $intent with activity: $this")
    navController.handleDeepLink(intent)
}

通过这种配置,我注意到了一些奇怪的行为:

每次单击深度链接时,WelcomeActivity都会收到两次onNewIntent调用。甚至有时会创建该活动的新实例。.

1_ object1-onNewIntent

2_ object1-onNewIntent

3_ object2-onCreate

这里有一些日志:

首次启动

onCreate:意图{flg = 0x10000000 具有活动的cmp = {applicationId} / {package} .WelcomeActivity}: {package}。WelcomeActivity@ 4adbef0

打开深层链接

onNewIntent:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x10010000 cmp = {applicationId} / {package}。WelcomeActivity(具有 活动):{package} .WelcomeActivity @ 4adbef0

onNewIntent:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x1001c000 cmp = {applicationId} / {package}。WelcomeActivity(具有 活动):{package} .WelcomeActivity @ 4adbef0

onCreate:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x1001c000 cmp = {applicationId} / {package}。WelcomeActivity(具有 活动):{package} .WelcomeActivity @ b77c6b

杀死该应用并打开深层链接

onCreate:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x10018000 cmp = {applicationId} / {package}。WelcomeActivity(具有 活动):{package} .WelcomeActivity @ b78f4df

onNewIntent:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x1001c000 cmp = {applicationId} / {package}。WelcomeActivity(具有 活动):{package} .WelcomeActivity @ b78f4df

onCreate:意图{act = android.intent.action.VIEW cat = [android.intent.category.BROWSABLE] dat = https:// {depp_link} ... flg = 0x1001c000 cmp = {applicationId} / {package}。WelcomeActivity(具有 {package} .WelcomeActivity @ dfe87b2

更新:

1-似乎启动模式与此问题无关。我注意到默认启动模式也是如此。

2- navController.navigate(intent.dataString.toUri())似乎工作正常。所以我想问题出在navController.handleDeepLink(intent)。

5 个答案:

答案 0 :(得分:2)

通过测试不同的更改,我得出的结论是“ navController.handleDeepLink(intent)”正在引起这种奇怪的行为。

这是我尝试过的:

我从导航中删除了deepLink,并且深链接正常工作(我已经手动添加了deepLink),并且具有正常的行为:使用singleTask,如果已经创建了活动,则onNewIntent仅被调用一次。如果未创建活动,则调用onCreate。

与此有关的另一个问题是,将在onCreate中自动调用navController.handleDeepLink(intent)(可以在javadocs中进行检查)。调用onNewIntent时,您需要调用navController.handleDeepLink(intent)。

我决定尝试使用NavController中的“ navigate(Uri deepLink)”,并且看到它按预期方式工作(第一段中描述的行为)。 有了这种替代方法,我决定进行一些更改:

class WelcomeActivity : AppCompatActivity(){

      private val navController by lazy { findNavController(R.id.nav_host_fragment) }

      private var deepLinkData: Uri? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Timber.d("onCreate: $intent  with activity: $this")
        val data = intent.data
        intent.data = null
        setContentView(R.layout.activity_welcome)
        handleDeepLink(data)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        Timber.d("onNewIntent: $intent with activity: $this")
        setIntent(intent)
        val data = intent?.data
        handleDeepLink(data)
    }

    private fun handleDeepLink(uri: Uri?) {
        //TODO: there is an issue that will cause onNewIntent to be called twice when the activity is already present.
        if (uri != null && deepLinkData.toString() != uri.toString() && navController.graph.hasDeepLink(uri)) {
            //possible deep link for LoginFragment
            deepLinkData = uri
            navController.navigate(uri)
        }
    }

}

在onCreate中注意以下代码块很重要:

val data = intent.data
intent.data = null

之所以这样,是因为如果我需要防止调用“ navController.handleDeepLink(intent)”,因为如果存在该信息,它将被自动调用,从而导致异常行为。

navController.graph.hasDeepLink(uri)将帮助您查看图形是否可以处理该uri。如果不使用它,则“ navigate(Uri deepLink)”将引发异常。

如果您遇到相同的问题,希望它可以为您提供帮助。如果您对此有更多了解,请随时发表评论。

答案 1 :(得分:1)

单击隐式深层链接时,FLAG_ACTIVITY_NEW_TASK的设置是有意的。并且根据文档,将重新创建堆栈,使其处于良好状态。请参见Implicit deep link这里的文档。

如果您不希望这种行为,并且不想更改意图标志,则可以自己处理深层链接,而不用将其传递给导航组件。因此,如果您使用的是AdvancedNavigationSample,则需要删除handledeeplink并按findNavController().navigate()进行操作。

还请记住将启动器活动的启动模式设置为singleTask,以接收有关单击Deepink或通知的新意图。

  1. 将示例中的NavController.handledeeplink()替换为
fun BottomNavigationView.navigateDeeplink(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    uri: Uri
) {
    navGraphIds.forEachIndexed { index, navGraphId ->
        val fragmentTag = getFragmentTag(index)

        // Find or create the Navigation host fragment
        val navHostFragment = obtainNavHostFragment(
            fragmentManager,
            fragmentTag,
            navGraphId,
            containerId
        )
        // Handle deeplink
        val canHandleDeeplink = navHostFragment.navController.graph.hasDeepLink(uri)

        if (canHandleDeeplink) {
            if (selectedItemId != navHostFragment.navController.graph.id) {
                selectedItemId = navHostFragment.navController.graph.id
            }
            navHostFragment.lifecycleScope.launchWhenResumed {
                // Wait for fragment to restore state from backStack
                // otherwise navigate will be ignored
                // Ignoring navigate() call: FragmentManager has already saved its state
                navHostFragment.navController.navigateOnce(uri)
            }
        }
    }
}

  1. intent.data存储在变量中,并将其设置为null,以使导航组件无法处理它。也可以在onHandleNewIntent
  2. 中执行相同的操作
  3. 将此深度链接传递到底部导航进行处理。并且如果该应用已在运行,则可以使用上述功能直接导航到目标位置。

完整的解决方案在此sample上实现。

优点:

  1. 您可以以相同方式直接处理通知点击。根据操作创建一个深层链接,并将其传递到PendingIntent中的intent.data中。
  2. 您可以根据需要验证和更改深度链接。例如,如果用户未登录,则直接进入登录活动,否则移至指定的目的地。

答案 2 :(得分:1)

onNewIntent上的回调首次到达时,只需设置标志intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);并将变异的意图传递给handleDeepLink(intent);

此标志消除了onNewIntent回调的第二次到达,因为它重新连接到现有的Activity(通过将堆栈跟踪完全重建到所需的深层链接目标),而不是启动新的Activity

详细信息位于handleDeepLink方法的源代码中。

答案 3 :(得分:0)

这感觉很奇怪,但实际上似乎按预期工作。 The documentation关于隐式深层链接说:

触发隐式深层链接时,后退堆栈的状态取决于是否使用Intent.FLAG_ACTIVITY_NEW_TASK标志启动了隐式Intent:

如果设置了该标志,则清除任务后退堆栈并替换为深层链接目标...。

如果未设置该标志,则将保留在先前应用程序的任务堆栈上,在该任务堆栈上触发了隐式深层链接。

对于您来说,我相信当您点击链接时,意图已经设置了标志Intent.FLAG_ACTIVITY_NEW_TASK,因此将创建整个新堆栈。当前the implementation只是使用新创建的堆栈重新启动活动,以确保任务状态一致。

如果您不需要这种行为,一种可能的解决方法是在导航组件有机会处理之前,从意图中清除标志Intent.FLAG_ACTIVITY_NEW_TASK

答案 4 :(得分:0)

我遇到了类似的问题,我关注的是BottomNavigationView中有多个NavHostFragment的{​​{3}}。因此,将我的补丁发布给那些案件相同的人。

更具体地说,通过通知深层链接启动应用程序时,我的MainActivity的onCreate()被调用了两次。

我为意图设置了以下标志:

private fun getPendingIntent(data: Uri?): PendingIntent {
    val intent = Intent(context, MainActivity::class.java)
    intent.action = Intent.ACTION_VIEW
    intent.data = data
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
    return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
}

我在MainActivity的清单中没有android:launchMode="singleTask",因为它对我的情况没有帮助。

我已经解决了我的问题,首先使用以下代码(感谢您)阻止了navController.handleDeepLink(intent)在MainActivity的onCreate()上被自动调用:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val uri = intent.data
    intent.data = null
}

,然后在NavigationExtensions文件中的NavigationAdvancedSample中进行更改,该文件基本上将navController.handleDeepLink(intent)替换为navController.navigate(uri),这导致活动被创建为两次,如您在您的注释中所述帖子。

因此,使用BottomNavigationView设置导航的代码在MainActivity中如下所示:

private fun setupBottomNavigation(uri: Uri?) {
    val navGraphIds =
        listOf(
            R.navigation.all_integrations,
            R.navigation.favourites,
            R.navigation.settings
        )

    currentNavController = bottomNavView.setupWithNavController(
        navGraphIds,
        supportFragmentManager,
        R.id.navHostContainer
    )

    uri?.let {
        bottomNavView.handleDeepLinks(
            navGraphIds,
            supportFragmentManager,
            R.id.navHostContainer,
            it
        )
    }
}