android - Kotlin 协程:androidx.lifecycle.MutableLiveData 对象上的 NullPointerException
问题描述
我正在尝试使用 kotlin Coroutines 使用 androidx.room 工件从数据库中获取数据。我已经分析了代码,但我还没有找到解决问题的方法。
tonight
我已经设置为可为空的对象出现空异常。我不应该在可空对象上得到空异常。
这是我正在编写用于获取数据的逻辑的 ViewModel 类
SleepTrackerViewModel.kt
package com.google.samples.apps.trackmysleepquality.sleeptracker
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.viewModelScope
import com.google.samples.apps.trackmysleepquality.database.SleepDatabaseDao
import com.google.samples.apps.trackmysleepquality.database.SleepNight
import com.google.samples.apps.trackmysleepquality.formatNights
import kotlinx.coroutines.launch
/**
* ViewModel for SleepTrackerFragment.
*/
class SleepTrackerViewModel(
val database: SleepDatabaseDao,
application: Application
) : AndroidViewModel(application) {
init {
initializeTonight()
}
/**
* [tonight] is the object that holds the most recent [SleepNight]
*/
private var tonight = MutableLiveData<SleepNight?>()
/**
* Get all the nights from the database
*/
private val nights = database.getAllNights()
val nightsString = Transformations.map(nights) { nights ->
formatNights(nights, application.resources)
}
private fun initializeTonight() {
viewModelScope.launch {
tonight.value = getTonightFromDatabase()
}
}
private suspend fun getTonightFromDatabase(): SleepNight? {
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
// If the start and end times are not the same, meaning that the night has already been completed
night = null
}
return night
}
/**
* Function to start tracking a new SleepNight
*/
fun onStartTracking() {
viewModelScope.launch {
val newNight = SleepNight()
insert(newNight)
//assign newNight to tonight as the most recent SleepNight
tonight.value = getTonightFromDatabase()
}
}
private suspend fun insert(night: SleepNight) {
database.insert(night)
}
fun onStopTracking() {
viewModelScope.launch {
val oldNight = tonight.value ?: return@launch
oldNight.endTimeMilli = System.currentTimeMillis()
update(oldNight)
}
}
private suspend fun update(night: SleepNight) {
database.update(night)
}
fun onClear() {
viewModelScope.launch {
clear()
tonight.value = null
}
}
suspend fun clear() {
database.clear()
}
}
错误信息
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.google.samples.apps.trackmysleepquality, PID: 21352
java.lang.NullPointerException: Attempt to invoke virtual method 'void
androidx.lifecycle.MutableLiveData.setValue(java.lang.Object)' on a null
object reference
at
com.google.samples.apps.trackmysleepquality.sleeptracker.SleepTrack
erViewModel$initializeTonight$1.invokeSuspend(SleepTrackerViewMod
el.kt:56)
at
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Contin
uationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
解决方案
这是在 Kotlin 中获得 NullPointerException 的几种意想不到的方法之一。您有一个在初始化之前init
调用该函数的块。当一个类被实例化时,所有的属性初始化和块都按从上到下的顺序被调用。initializeTonight()
tonight
init
您可能认为它是安全的,因为 的值tonight
是在协程中设置的,但默认情况下viewModelScope
会同步开始运行已启动协程的一部分,因为它使用Dispatchers.Main.immediate
. 您getTonightFromDatabase()
也在调用挂起函数,但该数据库也可能正在使用Dispatchers.Main.immediate
并且能够在不实际挂起的情况下返回结果。
我会按如下方式更改您的代码。删除init
块和initializeTonight
功能。像这样声明tonight
:
private var tonight = MutableLiveData<SleepNight?>().also {
viewModelScope.launch {
it.value = getTonightFromDatabase()
}
}
另外,我会做它val
而不是var
. 不应该有更换它的理由,所以让它var
很容易出错。
推荐阅读
- qt - SVG 图层转换为位图
- image - 图片在 Firebase 存储上上传了 4 次
- python - 列表和 .pop() 方法的行为:意外行为。(CPython 3.6)
- webpack - VSCode 扩展从开发中运行,但在使用 webpack 打包时不起作用
- mysql - 更新特定数据库(Mysql)的每个表中特定列的所有行
- html - 当我减小屏幕尺寸时,如何均匀地修剪图像的左右两侧?(@媒体查询)
- python - discord.py:你能从他们的 id 中获取不在你的机器人服务器中的用户的名字吗?
- c# - 我如何获得完全像这样的菜单控制?
- c# - 尝试使用 TryValidateModel 验证控制器中的输入对象时单元测试失败
- yocto - 如何使用 Yocto 修改 rootfs 配置文件?