加载带有可空参数的起始目标时,Jetpack导航组件将引发IllegalStateException

时间:2019-08-19 15:31:52

标签: android kotlin android-jetpack android-jetpack-navigation

我在起始目标位置添加了一个可为空的参数:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/nav_graph"
        app:startDestination="@id/startDest">

    <fragment android:id="@+id/startDest"
          android:name="com.myapp.MyStartFragment"
          android:label="Start"
          tools:layout="@layout/fragment_start">
        <argument
            android:name="dataObject"
            app:argType="com.myapp.MyDataObject"
            android:defaultValue="@null"
            app:nullable="true"/>
        ...
    </fragment>
    ...
</navigation>

但是当我加载应用程序时,出现以下异常:

java.lang.IllegalStateException: Fragment MyStartFragment{a4ffd1f (ca52d4dc-ff36-4a93-8ebf-f11af7b7d5aa) id=0x7f080145} has null arguments
    at com.myapp.MyStartFragment$$special$$inlined$navArgs$1.invoke(FragmentNavArgsLazy.kt:42)
    at com.myapp.MyStartFragment$$special$$inlined$navArgs$1.invoke(Unknown Source:0)
    at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:44)
    at androidx.navigation.NavArgsLazy.getValue(NavArgsLazy.kt:34)
    at com.myapp.MyStartFragment.getArgs(Unknown Source:27)
    at com.myapp.MyStartFragment.onAttach(MyStartFragment.kt:85)

该异常是由MyStartFragment中的这段代码触发的:

private val args: MyStartFragmentArgs by navArgs()
override fun onAttach(context: Context) {
    super.onAttach(context)
    val title = if(this.args.dataObject == null) getString(R.string.start_list_title) else this.args.dataObject!!.name
    ...
}

这是MyDataObject的代码:

@Parcelize
data class MyDataObject (
    val id: String,
    val name: String,
    val externalIdentifier: String
    val type: MyDataEnumType,
    var responsibleUser: SomeOtherParcelableClass?
): Parcelable 

我不明白的是导航控制器没有正确地传递我的起始目的地参数。我在这里想念东西吗?

2 个答案:

答案 0 :(得分:4)

你好,我想你想实现以下目标

BeforeFragment --arg--> StartFragment --> AfterFragment

sample nav graph

此流程类似于初次使用的用户,也就是返回的用户流程。这里BeforeFragmentlogin_nav_graph嵌套图中的最后一个片段。 StartFragmentmain_nav_graph的起始目的地。 StartFragment是用户返回的第一个屏幕。

因此,您可以在BeforeFragment中将args设置如下

val userJohn:User = User(34, "John", 645, UserType.TYPE2, Guardian("Mike"))
val action = BeforeFragmentDirections.actionGlobalStart(userJohn)
findNavController().navigate(action)

StartFragment中,您可以像已经做的那样进行跟踪

title = if(this.args.user == null)
   getString(R.string.user_name) // mocks loading saved user name
else
   this.args.user?.name // when user is first time user read from passed args

可以找到样本回购here


我的最佳猜测

此问题是由于旧版导航中的错误所致,请使用示例存储库中使用的2.2.0-alpha01

为了解决移至新导航版本时发生的错误 在您的模块gradle文件中添加以下内容

android {
    ...
    kotlinOptions {
        jvmTarget = "1.8" // set your Java version here
    }
}

这可以纠正错误

  

无法内联字节码...


不建议您记住将复杂对象作为参数传递。引自docs

  

通常,您应该强烈建议只传递最小的金额   目的地之间的数据量。例如,您应该将密钥传递给   检索对象而不是传递对象本身,作为总数   在Android上,所有已保存状态的空间都受到限制。如果您需要通过   大量数据,请考虑使用ViewModel中所述   在片段之间共享数据。

如果这可以解决您的问题,请确认答案,因为我花了大量时间准备这篇文章。

答案 1 :(得分:0)

如果您创建一个带有捆绑软件的片段,但没有NavController.navigate,并且您仍然希望将“ by navArgs()”保留在目标片段中,例如,因为要在测试时在launchFragmentInContainer中使用它,请使用try / catch 。在开始检查可为空的类型之前,将引发IllegalStateException。

private fun updateArguments(){
    this.title = try{
        val args: DestinationFragmentArgs by navArgs()

        if (args.dataObject?.name == ""){
            // default value provided so check whether bundle was used
            arguments?.getString("name") ?: ""
        }else {
            args?.venueId
        }
    }catch (ex: Exception){
        // fragment created without using NavController.navigate
        arguments?.getString("venueId") ?: ""
    }
}

房间非常快,因此从id检索对象作为参数不成问题,因此,如果可能,请尝试对参数使用基本类型,否则对廉价对象使用Parcelable或Serialize。