android - 为什么 ViewModel 和 Fragment 中的相同变量不同?
问题描述
我正在尝试按此顺序完成这些步骤。
- 用数据填充我的 Room 数据库。
- 使用当前数据库内容更新列表变量(在 ViewModel 中)。
- 从我的 Fragment 访问列表变量(在 ViewModel 中)。
执行后,我按此顺序获得 Toasts
- 0 在 MythFragment
- 5 在 MythViewModel
我期望的是相同数量的元素。是否因为协程而有所不同?我必须使用 LiveData 吗?
神话片段.kt
class MythFragment : Fragment() {
private lateinit var viewModel: MythViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_myth, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MythViewModel::class.java)
viewModel.populateMyths()
viewModel.fetchFromDatabase()
Toast.makeText(activity, "${viewModel.myths.size} in MythFragment", Toast.LENGTH_SHORT)
.show()
}
}
MythViewModel.kt
class MythViewModel(application: Application) : BaseViewModel(application) {
var myths = listOf<Myth>()
fun populateMyths() {
launch {
val dao = MythDatabase(getApplication()).mythDao()
if (dao.getRowCount() > 0)
return@launch
val mythList = listOf<Myth>(
Myth("This is the myth 1", "This is the evaluation of the myth 1"),
Myth("This is the myth 2", "This is the evaluation of the myth 2"),
Myth("This is the myth 3", "This is the evaluation of the myth 3"),
Myth("This is the myth 4", "This is the evaluation of the myth 4"),
Myth("This is the myth 5", "This is the evaluation of the myth 5")
)
dao.insertAll(
*mythList.toTypedArray()
)
}
}
fun fetchFromDatabase() {
launch {
myths = MythDatabase(getApplication()).mythDao().getAllMyths()
Toast.makeText(getApplication(), "${myths.size} in MythViewModel", Toast.LENGTH_SHORT)
.show()
}
}
}
BaseViewModel.kt
abstract class BaseViewModel(application: Application) : AndroidViewModel(application),
CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
解决方案
我期望的是相同数量的元素。是否因为协程而有所不同?我必须使用 LiveData 吗?
是的,每次使用时,您都会launch
在另一个线程上运行一些代码。这就是为什么你的Fragment
. 最好使用LiveData
,以便您在数据更改时收到通知。
在你的ViewModel
你可以做这样的事情:
private val _myths = MutableLiveData<Myth>() // keep the mutable one private
val myths: LiveData<Myth> = _myths // expose an immutable one
fun fetchFromDatabase() {
launch {
_myths.postValue(MythDatabase(getApplication()).mythDao().getAllMyths())
Toast.makeText(getApplication(), "${myths.size} in MythViewModel", Toast.LENGTH_SHORT).show()
}
}
在您的 Fragment 中,您观察到神话的变化:
...
viewModel.populateMyths()
viewModel.fetchFromDatabase()
viewModel.myths.observe(viewLifecycleOwner, Observer { myths ->
Toast.makeText(activity, "${myths.size} in MythFragment", Toast.LENGTH_SHORT).show()
})
您可以使用构建器使上述代码更简洁livedata
(需要androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0
or up 依赖项):
val myths = livedata {
emit(MythDatabase(getApplication()).mythDao().getAllMyths())
}
然后也没有必要再打电话fetchFromDatabase()
了。
推荐阅读
- docker - Hadoop。如何避免由于 Docker 自动命名而导致的 worker 文件
- python - 如何 np.save 从输入路径到输出路径?
- javascript - 如何等待在 Selenium 中为 Angular 完成所有 http 调用?
- python - 用十六进制制表打印值
- google-cloud-platform - 如何将 SNAPPY 格式的压缩拼花数据从 Google Storage 存储桶加载到 BigQuery 表
- r - R中的日期格式
- sql - 如何从视图中唯一标识表,并将 1:1 和逻辑与视图分开?
- bash - 使用包含 NULL 字符的正则表达式字符范围的 Grep
- kotlin - Unit.INSTANCE 与单位?Unit 编译但有 IDE 错误,Unit.INSTANCE 没有编译但没有 IDE 错误
- r - 整合两个数据框,给出一个位置并在 R 中使用一个区间