从ViewModel观察Android LiveData <T>返回NULL

时间:2020-06-27 13:56:38

标签: android kotlin android-livedata android-viewmodel android-livedata-transformations

我有一个ViewModel,它可以显示transactionstransactionSummary LiveData

// Transactions
val transactions: LiveData<List<Transaction>> = getTransactionByDateUseCase
    .getTransactionsByDate(dateRange.startDate, dateRange.endDate)

// Transaction Summary
private var _transactionsSummary = MutableLiveData<TransactionSummary>()
val transactionsSummary: LiveData<TransactionSummary> = _transactionsSummary

transactions来自 Room ,它在LiveData<T>中类似地返回transactionSummary,但是存在转换过程以使交易摘要为income,并且expense

下面是转换过程:

private fun getTransactionsSummary(dateRange: DateRange): LiveData<TransactionSummary> {
    return Transformations.map(
        getTransactionByDateUseCase.getTransactionsByDate(dateRange.startDate, dateRange.endDate)
    ) { data ->
        getTransactionSummaryUseCase.transactions = data
        val summary = getTransactionSummaryUseCase.getSummaries(dateRange)
        summary
    }
}

但是,当我请求片段更新时,transactionSummary总是返回NULL

getFetchTransactionViewModel().updateTransactionSummary(selectedDateRange)

这是我在ViewModel中的更新功能:

fun updateTransactionSummary(dateRange: DateRange) {
    _transactionsSummary.value = getTransactionsSummary(dateRange).value
}

我这样观察transactionSummary

    getFetchTransactionViewModel().transactionsSummary.observe(this, Observer {
            textViewIncome.text = it.totalIncome.formatCurrency(isVisibleCurrency = true)
            textViewExpense.text = it.totalExpense.formatCurrency(isVisibleCurrency = true)
            textViewBalance.text = (it.totalIncome - it.totalExpense).formatCurrency(isVisibleCurrency = true)
    })

是否缺少任何步骤或实施不正确?

谢谢

2 个答案:

答案 0 :(得分:0)

我自己遇到了完全相同的问题。似乎仅在第一个观察者订阅它们后(活动状态),使用Transformations.map创建的LiveData对象(通常可能是MediatorLiveData)才初始化它们的值。在此之前,即使源LiveData具有值,它也将返回值null(非活动状态)。这是在您的函数updateTransactionSummary()中发生的。

解决方案1:您可以直接使用转换后的LiveData,而不是尝试从中提取值并将其放入另一个MutableLiveData中。像这样:

private var _transactionsSummary = getTransactionsSummary(dateRange)
val transactionsSummary: LiveData<TransactionSummary> = _transactionsSummary

那么您将不需要updateTransactionSummary()函数。

此外,您可以直接将事务LiveData用作转换的来源,而不必进行第二次转换:

private fun getTransactionsSummary(dateRange: DateRange): LiveData<TransactionSummary> {
    return Transformations.map(transactions) { data ->
        getTransactionSummaryUseCase.transactions = data
        val summary = getTransactionSummaryUseCase.getSummaries(dateRange)
        summary
    }
}

解决方案2:在您的评论中,您说您想通过另一个事件来触发摘要更新。在这种情况下,使用LiveData转换似乎是多余的:

private fun getTransactionsSummary(dateRange: DateRange): TransactionSummary {
   getTransactionSummaryUseCase.transactions = transaction.value
   return getTransactionSummaryUseCase.getSummaries(dateRange)
}

fun updateTransactionSummary(dateRange: DateRange) {
    _transactionsSummary.value = getTransactionsSummary(dateRange)
}

答案 1 :(得分:0)

fun updateTransactionSummary(dateRange: DateRange) {
    _transactionsSummary.value = getTransactionsSummary(dateRange).value
}

这不是使用LiveData的正确方法。

DateRange也是一个可变参数,必须作为输入传播到异步数据加载。

这意味着必须将其包含在MutableLiveData<DateRange>中,然后.switchMap放入正确的查询结果中。

// Transaction Summary
//private var _transactionsSummary = MutableLiveData<TransactionSummary>()

private val dateRange = MutableLiveData<DateRange>()

val transactionsSummary: LiveData<TransactionSummary> = dateRange.switchMap { date ->
    getTransactionsSummary(dateRange)
}

很有可能在整个过程中都不会忘记DateRange,在这种情况下,如果DateRange实际上是ParcelableSerializable,则它应该自动坚持使用SavedStateHandle中的ViewModel捆绑在一起,并尽可能使用private val dateRange = savedStateHandle.getLiveData<DateRange>("dateRange")