Solution 1 :
Why does this happen? This happens because your ViewModel
has not been killed yet. The ViewModel
on cleared()
is called when the Fragment
is destroyed. In your case your app is not killed and LiveData
would just emit the latest event already set. I don’t think this is a case to use liveData
builder. Just execute the method in the ViewModel
when your Fragment gets in onResume()
:
override fun onResume(){
viewModel.checkData()
super.onResume()
}
// in the viewmodel
fun checkData(){
_yourMutableLiveData.value = Resource.loading(null)
try {
val data = repository.getCategories()
repository.insert(data)
_yourMutableLiveData.value = Resource.success(data)
} catch (e: Exception) {
val data = repository.getCategories()
if (!data.isNullOrEmpty()) {
_yourMutableLiveData.value = Resource.success(data)
} else {
val ex = handleException(e)
_yourMutableLiveData.value = Resource.error(ex,null)
}
}
}
Not sure if that would work, but you can try to add the listener directly in onResume()
but careful with the instantiation of the ViewModel
.
Small advice, if you don’t need a value like in Resource.loading(null)
just use a sealed class
with object
UPDATE
Regarding your question that you ask why it works with a function and not with a variable, if you call that method in onResume
it will get executed again. That’s the difference. Check the Fragment
or Activity
lifecycle before jumping to the ViewModel
stuff.
Problem :
I have a usecase:
- Open app + disable network -> display error
- Exit app, then enable network, then open app again
Expected: app load data
Actual: app display error that meaning state error cached, liveData is not emit
Repository class
class CategoryRepository(
private val api: ApiService,
private val dao: CategoryDao
) {
val categories: LiveData<Resource<List<Category>>> = liveData {
emit(Resource.loading(null))
try {
val data = api.getCategories().result
dao.insert(data)
emit(Resource.success(data))
} catch (e: Exception) {
val data = dao.getCategories().value
if (!data.isNullOrEmpty()) {
emit(Resource.success(data))
} else {
val ex = handleException(e)
emit(Resource.error(ex, null))
}
}
}
}
ViewModel class
class CategoryListViewModel(
private val repository: CategoryRepository
): ViewModel() {
val categories = repository.categories
}
Fragment class where LiveDate obsever
viewModel.apply {
categories.observe(viewLifecycleOwner, Observer {
// live data only trigger first time, when exit app then open again, live data not trigger
})
}
can you help me explain why live data not trigger in this usecase and how to fix? Thankyou so much
Update
I have resolved the above problem by replace val categories
by func categories()
at repository class. However, I don’t understand and can’t explain why it works properly with func
but not val
.
Comments
Comment posted by coroutineDispatcher
do you kill the app or not?
Comment posted by Nguyễn Văn Quang
I exit app (press button back) and I debug and sure that Fragment(Activity) is destroyed, ViewModel is cleared. But I not kill app from task list.
Comment posted by Nguyễn Văn Quang
I try exit app and if I kill app from task list or clear app cache then open app again, app load data success. why? I don’t understand
Comment posted by Nguyễn Văn Quang
I exit app (press button back) and I debug and sure that Fragment(Activity) is destroyed, ViewModel is cleared. But I not kill app from task list.
Comment posted by Nguyễn Văn Quang
I try exit app and if I kill app from task list or clear app cache then open app again, app load data success. why? I don’t understand.
Comment posted by coroutineDispatcher
Because
Comment posted by Nguyễn Văn Quang
I press button back to exit app. So I sure that activity called
Comment posted by coroutineDispatcher
No. If you press back the