Android 为什么我的ViewModel在Fragment中为空,而不是Fragment';什么是绑定布局?

Android 为什么我的ViewModel在Fragment中为空,而不是Fragment';什么是绑定布局?,android,kotlin,layout,fragment,viewmodel,Android,Kotlin,Layout,Fragment,Viewmodel,我有一个FooActivity:AppCompatActivity(),它使用FooViewModel从数据库中查找Foo,然后在一些片段中显示有关它的信息。我是这样设置的: private lateinit var viewModel: FooViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Get the Intent t

我有一个
FooActivity:AppCompatActivity()
,它使用
FooViewModel
从数据库中查找
Foo
,然后在一些
片段中显示有关它的信息。我是这样设置的:

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
}
fooinforagment
类中:

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}

问题是在绑定后尝试访问
viewModel.foo.value?.name
时,在我的
fooinforagment.onCreateView
中,
viewModel
为空

    binding.viewModel = viewModel
    Log.wtf(TAG, "${viewModel.foo.value?.name} shouldn't be null!")
    return binding.root
我不明白为什么它在我的片段中是空的,但在我的布局中不是空的。帮助?

FooInfoFragment片段中的“viewModel”尚未初始化,这就是为什么它在片段中为空

binding.viewModel = viewModel 
//ViewModel is not initialized 

从这里的字里行间可以看出,您的问题在于,您不希望
viewModel.foo.value
为null,而不是在访问
viewModel.foo
时,为null
viewModel
获得Java NPE

如果
foo
是LiveData,则其
参数始终可为空,即使其类型不是。这就是LiveData类的定义方式<代码>值
可为空,因为它在设置之前默认为空。设置后,它将不再为空。如果您确定在访问它之前总是设置它的值,那么可以使用
value
值?:错误(“值在设置之前已被访问。”)

或者,您可以编写扩展属性以更干净地访问它:

val <T: Any> LiveData<T>.requiredValue: T get() = 
    value ?: error("Non-null value cannot be accessed before value is first set.")
val LiveData.requiredValue:T get()=
值?:错误(“首次设置值之前无法访问非空值。”)

但是,很少需要直接访问LiveData值。LiveData的目的是观察对值的更改,在这种情况下,您永远不会遇到此问题。

activityViewModels()
(我想这是fragemnt ktx提供的)有自己的实现,我认为它无法创建一个,因为您有一个参数化构造函数。@ADM我知道viewModel是由activityViewModels()创建的,因为绑定后我看到
Foo
信息在
片段
布局中呈现
activityViewModels()
使用
ViewModelFactory
创建
ViewModel
。看,viewmodel是否真的为空?在行
viewModel.foo.value?.name
中,它也可以是
foo
foo.value
。如果它是
viewmodel
viewmodel.foo
,应用程序将崩溃。
viewmodel
本身为空?或者
viewModel.foo.value?.name
为空?如果
viewModel.foo.value
则可能是因为尚未设置实时数据。Aka数据库中的数据尚未加载。
by activityViewModels()
保证在您第一次访问时对其进行初始化。非常感谢您让我知道。。现在@Tenfour04 viewModel.LiveData.value在Observer之外将始终为null,即使它不可为null(我通过艰苦的学习了解到了这一点)。它只能确认它在观察者中保存数据。如果您想使用一次性异步操作,请使用Flow或Transformations.switchMap。谢谢您的提示。根本原因是,从数据库中获取Foo的异步操作在onCreate返回时没有终止,而是在呈现之前终止(因此布局中的所有LiveData都正确呈现,这使我忘记了这些数据的可用性取决于异步操作)。这里需要帮助-->