在Kotlin Flow For Room数据库中处理NPE



我想从Room数据库中检索单个对象,所以我在Dao 中有这个方法

// in Dao
@Query("SELECT * FROM table_foo ORDER BY RANDOM()")
fun getSingleFoo(): Flow<FooEntity>

然后,该对象将被映射到其他模型中,比如PlainFoo

// in Repository
fun getRandomFoo(): Flow<PlainFoo> = dao.getSingleFoo()
.map(FooEntity::asExternalModel)

但在这个应用程序的第一次发布中,表格是空的。它使dao函数返回null,并在映射时触发NPE。我试着把它包在这样一个密封的接口里。

// Result.kt as wrapper
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Error(val exception: Throwable? = null) : Result<Nothing>
}
fun <T> Flow<T>.asResult(): Flow<Result<T>> = this
.map<T, Result<T>> {
Result.Success(it)
}
.catch {
emit(Result.Error(it))
}

然后我在表示层中这样调用这个方法。

// in ViewModel
val randomFoo = fooRepository.getRandomFoo().asResult()
// in activity, log only for checking
lifecycleScope.launch {
viewModel.randomFoo.collect {
Timber.tag("RandomFooFlow").d("$it")
}
}

它捕捉到错误,看起来像这样。

Error(exception=java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter <this>)

但是,当插入新数据时,它不会得到更新,除非我重新打开应用程序(这意味着正在收集新的Flow,而不是旧的Flow(。看来流量被取消了。

有没有办法在不让我的刀返回的情况下处理这个问题可以为null的对象?

注意:如果在打开应用程序时已经填充了数据,则流能够继续消耗新值(。

与其处理异常,我建议从您的Dao返回可为null的类型。然后,您还可以更新映射器函数来处理类型为null的能力。您不需要将它封装到任何Result类中,只需在UI端进行简单的null检查就足够了。

// Dao
@Query("SELECT * FROM table_foo ORDER BY RANDOM()")
fun getSingleFoo(): Flow<FooEntity?>
// Repo
fun getRandomFoo(): Flow<PlainFoo?> = dao.getSingleFoo().map { it?.asExternalModel() }

您能从视图模型中的协程内部调用存储库getRandomFoo((方法吗?此外,您还需要使用LiveData或StateFlow等数据观察来调用响应。顺便说一句,您可以使用包装内部存储库来包装您的结果。在代码示例中,我并不关心它,因为您的错误与映射无关。

查看模型

private val _stateFlow = MutableStateFlow()
val stateFlow:StateFlow
fun getRandom(){
fooRepository.getRandomFoo().onEach{
if(it is Result.Success){
stateFlow.value = it
}
}.launchIn(viewModelScope)
}

片段或活动

viewLifecycleOwner.lifecycle.repeatOnLifecycle{ 
stateFlow.collect{
// Listen data for your UI
}
}

最新更新