I'm using the Jetpack Navigation as a navigation solution in my app.
I'm facing an inconsistent behaviour when using the generated safe args as opposed to the deeplink strategy.
Say I have following navigation graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@+id/home_dest">
<fragment
android:id="@+id/home_dest"
android:name="com.example.android.codelabs.navigation.HomeFragment"
android:label="@string/home"
tools:layout="@layout/home_fragment" >
<action
android:id="@+id/action_home_dest_to_deeplink_dest"
app:destination="@id/deeplink_dest" />
</fragment>
<fragment
android:id="@+id/deeplink_dest"
android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
android:label="@string/deeplink"
tools:layout="@layout/deeplink_fragment">
<argument
android:name="myarg"
app:argType="string" />
<deepLink app:uri="example://my-deelink-frag?myArg={myarg}" />
</fragment>
</navigation>
I have defined 2 ways to navigate to the DeepLinkFragment:
- Navigation action with id
action_home_dest_to_deeplink_dest - A deeplink
example://my-deelink-frag?myArg={myarg}
In the DeepLinkFragment I'm using the navArgs() function to de-serialize the incoming Bundle and show a simple Toast message.
class DeepLinkFragment : Fragment() {
private val args : DeepLinkFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Toast.makeText(requireContext(), args.myarg, Toast.LENGTH_SHORT).show()
}
}
When navigating with the generated safe args I can sent an empty string as the value for myArg.
No problem.
findNavController().navigate(HomeFragmentDirections.actionHomeDestToDeeplinkDest(""))
However, when navigating with an empty string as argument via the deeplink way:
val arg = ""
findNavController().navigate("example://my-deelink-frag?myArg=${arg}".toUri())
I get following stacktrace:
Caused by: java.lang.IllegalArgumentException: Required argument "myarg" is missing and does not have an android:defaultValue
at com.example.android.codelabs.navigation.DeepLinkFragmentArgs$Companion.fromBundle(DeepLinkFragmentArgs.kt:29)
at com.example.android.codelabs.navigation.DeepLinkFragmentArgs.fromBundle(Unknown Source:2)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:52)
at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:34)
at com.example.android.codelabs.navigation.DeepLinkFragment.getArgs(DeepLinkFragment.kt:30)
at com.example.android.codelabs.navigation.DeepLinkFragment.onViewCreated(DeepLinkFragment.kt:34)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2987)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:546)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2106)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8068)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
Which is coming from the generated DeeplinkFragmentArgs code
public data class DeepLinkFragmentArgs(
public val myarg: String = ""
) : NavArgs {
public fun toBundle(): Bundle {
val result = Bundle()
result.putString("myarg", this.myarg)
return result
}
public companion object {
@JvmStatic
public fun fromBundle(bundle: Bundle): DeepLinkFragmentArgs {
bundle.setClassLoader(DeepLinkFragmentArgs::class.java.classLoader)
val __myarg : String?
if (bundle.containsKey("myarg")) {
__myarg = bundle.getString("myarg")
if (__myarg == null) {
throw IllegalArgumentException("Argument \"myarg\" is marked as non-null but was passed a null value.")
}
} else {
__myarg = ""
}
return DeepLinkFragmentArgs(__myarg)
}
}
}
Looking at the code it seems as if an empty string is making the
bundle.getString("myarg")returnnullinstead of the empty string?!
As a fix I could add the android:defaultValue.
However I don't think is the right solution.
<argument
android:name="myarg"
android:defaultValue=""
app:argType="string" />
Is this a known limitation when using deeplinks?