Room架构Dao-Repository-ViewModel函数返回空值且运行崩溃问题排查
Let's break down the issues in your code step by step—there are a couple of critical mistakes causing the null return and crash:
1. The ViewModel is mishandling LiveData's asynchronous nature
This is the root cause of your null value problem. Look at your ViewModel code:
fun avgBaliseAJeun(balise: String): LiveData<Float> { val result = MutableLiveData<Float>() viewModelScope.launch(Dispatchers.IO) { val retour = repository.avgBaliseAJeun(balise) // ❌ Big mistake: directly accessing LiveData's value here result.postValue(retour.value) } return result }
The avgByBalise DAO method returns a LiveData, which runs its query asynchronously. When you call retour.value right after getting the LiveData, the query hasn't finished yet—so you're always getting null and posting that to your result LiveData. That's why your Fragment sees a null value and crashes.
Fix the ViewModel (two options):
Option 1: Return the original LiveData (simplest & recommended)
Since your DAO and Repository already handle returning a LiveData, you don't need to wrap it in another MutableLiveData. Just pass it through directly:
fun avgBaliseAJeun(balise: String): LiveData<Float> { return repository.avgBaliseAJeun(balise) }
LiveData automatically handles running the query in the background and notifying observers when data is ready—no coroutine hacks needed.
Option 2: Wrap with custom logic (only if needed)
If you need to modify the value before passing it along, observe the original LiveData properly (on the main thread, since LiveData requires this):
fun avgBaliseAJeun(balise: String): LiveData<Float> { val result = MutableLiveData<Float>() val sourceLiveData = repository.avgBaliseAJeun(balise) viewModelScope.launch(Dispatchers.Main) { sourceLiveData.observeForever { avgValue -> // Add any custom processing here result.value = avgValue ?: 0F // Guarantee no nulls // Clean up the observer to avoid memory leaks sourceLiveData.removeObserver { } } } return result }
Only use this if you have specific data transformation needs—otherwise stick to Option 1.
2. DAO type mismatch (easy to miss!)
Your DAO query has a subtle type issue:
@Query("SELECT IFNULL(avg(taux), 0.0) FROM diabete WHERE balise = :balise") fun avgByBalise(balise: String): LiveData<Float>
avg(taux) returns a Double, and 0.0 is also a Double—but you're declaring the return type as LiveData<Float>. This type mismatch can cause runtime conversion errors that result in null values.
Fix it by matching the types:
// Either change the return type to Double @Query("SELECT IFNULL(avg(taux), 0.0) FROM diabete WHERE balise = :balise") fun avgByBalise(balise: String): LiveData<Double> // Or use a Float literal to ensure consistency @Query("SELECT IFNULL(avg(taux), 0f) FROM diabete WHERE balise = :balise") fun avgByBalise(balise: String): LiveData<Float>
3. Fragment observation best practices
Your Fragment code uses observeAsState, which is a Jetpack Compose API—make sure you're using it inside a Composable function. If you're working with the traditional View system, use the standard observe method instead:
// In your Fragment's onViewCreated() dpViewModel.avgBaliseAJeun("À jeun").observe(viewLifecycleOwner) { avgValue -> // Use the average value here (e.g., update a TextView) avgValue?.let { // Handle valid value } ?: run { // Fallback (though your DAO's IFNULL should prevent this) } }
Final Summary of Fixes
- Correct the type mismatch in your DAO query to match the LiveData return type.
- Simplify your ViewModel to return the original LiveData from the Repository.
- Ensure you're observing the LiveData correctly in your Fragment (Composable or traditional View system).
These changes will eliminate the null values and stop your app from crashing.
内容的提问来源于stack exchange,提问作者Daniel Beauchamp

