Search code examples
androidandroid-jetpackandroid-architecture-navigationandroid-jetpack-navigation

Android navigation components with deep link: onNewIntent called multiple times


This time I need your help regarding the use of android navigation components with deeplink.

I have been following this documentation and the connection between fragment and deeplink is working fine.

The problem comes in regards to the activity that is receiving the deeplink. In my case, I set the 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)
}

With this configuration I noticed a couple of weird behaviours:

WelcomeActivity receives onNewIntent call two times every time I click the deeplink. Having even sometimes new instances of that activity created.. like

1_ object1-onNewIntent

2_ object1-onNewIntent

3_ object2-onCreate

Here you have some logs:

First launch

onCreate: Intent { flg=0x10000000 cmp={applicationId}/{package}.WelcomeActivity } with activity: {package}.WelcomeActivity@4adbef0

Open deep link

onNewIntent: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}… flg=0x10010000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with activity: {package}.WelcomeActivity@4adbef0

onNewIntent: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}... flg=0x1001c000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with activity: {package}.WelcomeActivity@4adbef0

onCreate: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}... flg=0x1001c000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with activity: {package}.WelcomeActivity@b77c6b

Kill the app and open deep link

onCreate: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}... flg=0x10018000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with activity: {package}.WelcomeActivity@b78f4df

onNewIntent: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}... flg=0x1001c000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with activity: {package}.WelcomeActivity@b78f4df

onCreate: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://{depp_link}... flg=0x1001c000 cmp={applicationId}/{package}.WelcomeActivity (has extras) } with {package}.WelcomeActivity@dfe87b2

UPDATE:

1 -It seems launch mode has nothing to do with this issue. I noticed the same with default launch mode.

2- navController.navigate(intent.dataString.toUri()) seems to work fine. So I guess the problem is navController.handleDeepLink(intent).


Solution

  • Testing different changes, I came to the conclusion that "navController.handleDeepLink(intent)" is causing this weird behaviour.

    This is what I tried:

    I removed the deepLink from the navigation, and the deep link was working just fine (I have added deepLink manually) with a normal behaviour: using singleTask, if the activity is already created, then onNewIntent is called only once. If the activity is not created, then onCreate is called.

    An extra problem with this is that navController.handleDeepLink(intent) will be called automatically in onCreate (you can check that in the javadocs). When onNewIntent is called, you need to call navController.handleDeepLink(intent).

    I decided to try "navigate(Uri deepLink)" from NavController and I see that is working as expected (behaviour described in the first paragraph). Having this alternative method, I decided to make some changes:

    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)
            }
        }
    
    }
    

    It is important to notice this block of code in onCreate:

    val data = intent.data
    intent.data = null
    

    The reason for this is because if I need to prevent "navController.handleDeepLink(intent)" to be called as it will be called automatically if that information is present, causing the weird behaviour.

    navController.graph.hasDeepLink(uri) will help you to see if your graph can handle that uri. If you do not use it, then "navigate(Uri deepLink)" will throw an exception.

    Hope it can help you if you are running into the same problem. If you have more insights on this, feel free to leave some comments.