为什么我的 ViewModel 在 Fragment 中为 null 而不是 Fragment 的绑定布局?
Why is my ViewModel null in Fragment but not Fragment's bound layout?
我有一个 FooActivity: AppCompatActivity()
,它使用 FooViewModel
从数据库中查找 Foo
,然后在 Fragment
秒内显示有关它的信息。以下是我的设置方式:
private lateinit var viewModel: FooViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Get the Intent that started this activity and extract the FOO_ID
val id = intent.getLongExtra(FOO_ID, 1L)
val viewModelFactory = FooViewModelFactory(
id,
FooDatabase.getInstance(application).fooDao,
application)
viewModel = ViewModelProviders.of(
this, viewModelFactory).get(FooViewModel::class.java)
// FooViewModel is bound to Activity's Fragments, so must
// create FooViewModelFactory before trying to bind/inflate layout
binding = DataBindingUtil.setContentView(this, R.layout.activity_foo)
binding.lifecycleOwner = this
binding.viewModel = viewModel
}
并且在 FooInfoFragment
class:
private val viewModel: FooViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState)
val binding = FragmentFooInfoBinding.inflate(inflater)
binding.setLifecycleOwner(this)
binding.viewModel = viewModel
// normally viewModel.foo shouldn't be null here, right?
return binding.root
}
到目前为止一切顺利。我的布局显示 Foo
的各种信息,例如 @{viewModel.foo.name}
.
问题是在我的 FooInfoFragment.onCreateView
中,当我尝试在绑定后访问 viewModel.foo.value?.name
时,viewModel
为空。
binding.viewModel = viewModel
Log.wtf(TAG, "${viewModel.foo.value?.name} shouldn't be null!")
return binding.root
我不明白为什么它在我的 Fragment 中为 null 但在我的布局中却没有。帮忙?
FooInfoFragment Fragment 中的“viewModel”尚未初始化,这就是它在 Fragment 中为 null 的原因。
binding.viewModel = viewModel
//ViewModel is not initialized
从这里的字里行间可以看出,您的问题是您不希望 viewModel.foo.value
可以为 null,而不是您得到一个 Java NPE for null viewModel
您访问 viewModel.foo
.
如果 foo
是一个 LiveData,它的 value
参数总是可以为 null,即使它的类型不是。这就是 LiveData class 的定义方式。 value
可以为空,因为它在设置之前默认为空。设置后,它不会再为空。如果你确定你总是在访问它之前设置它的值,你可以使用 value!!
或 value ?: error("Value was accessed before it was set.")
.
或者你可以写一个扩展属性来更干净地访问它:
val <T: Any> LiveData<T>.requiredValue: T get() =
value ?: error("Non-null value cannot be accessed before value is first set.")
但是,很少需要直接访问 LiveData 值。 LiveData 的目的是观察值的变化,在这种情况下,您永远不会 运行 进入这个问题。
我有一个 FooActivity: AppCompatActivity()
,它使用 FooViewModel
从数据库中查找 Foo
,然后在 Fragment
秒内显示有关它的信息。以下是我的设置方式:
private lateinit var viewModel: FooViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Get the Intent that started this activity and extract the FOO_ID
val id = intent.getLongExtra(FOO_ID, 1L)
val viewModelFactory = FooViewModelFactory(
id,
FooDatabase.getInstance(application).fooDao,
application)
viewModel = ViewModelProviders.of(
this, viewModelFactory).get(FooViewModel::class.java)
// FooViewModel is bound to Activity's Fragments, so must
// create FooViewModelFactory before trying to bind/inflate layout
binding = DataBindingUtil.setContentView(this, R.layout.activity_foo)
binding.lifecycleOwner = this
binding.viewModel = viewModel
}
并且在 FooInfoFragment
class:
private val viewModel: FooViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState)
val binding = FragmentFooInfoBinding.inflate(inflater)
binding.setLifecycleOwner(this)
binding.viewModel = viewModel
// normally viewModel.foo shouldn't be null here, right?
return binding.root
}
到目前为止一切顺利。我的布局显示 Foo
的各种信息,例如 @{viewModel.foo.name}
.
问题是在我的 FooInfoFragment.onCreateView
中,当我尝试在绑定后访问 viewModel.foo.value?.name
时,viewModel
为空。
binding.viewModel = viewModel
Log.wtf(TAG, "${viewModel.foo.value?.name} shouldn't be null!")
return binding.root
我不明白为什么它在我的 Fragment 中为 null 但在我的布局中却没有。帮忙?
FooInfoFragment Fragment 中的“viewModel”尚未初始化,这就是它在 Fragment 中为 null 的原因。
binding.viewModel = viewModel
//ViewModel is not initialized
从这里的字里行间可以看出,您的问题是您不希望 viewModel.foo.value
可以为 null,而不是您得到一个 Java NPE for null viewModel
您访问 viewModel.foo
.
如果 foo
是一个 LiveData,它的 value
参数总是可以为 null,即使它的类型不是。这就是 LiveData class 的定义方式。 value
可以为空,因为它在设置之前默认为空。设置后,它不会再为空。如果你确定你总是在访问它之前设置它的值,你可以使用 value!!
或 value ?: error("Value was accessed before it was set.")
.
或者你可以写一个扩展属性来更干净地访问它:
val <T: Any> LiveData<T>.requiredValue: T get() =
value ?: error("Non-null value cannot be accessed before value is first set.")
但是,很少需要直接访问 LiveData 值。 LiveData 的目的是观察值的变化,在这种情况下,您永远不会 运行 进入这个问题。