I am trying first handle the response from API by using observe. Later after observing the handled variable I want to save it to database.
The variable tokenFromApi
is updated inside tokenResponseFromApi
's observer. Is it possible to observe tokenFromApi
outside the observer of tokenResponseFromApi
? When debugged, the code did not enter inside tokenFromApi
observer when the app started.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
var tokenResponseFromApi: LiveData<String>? = MutableLiveData<String>()
var tokenFromApi: LiveData<TokenEntity>? = MutableLiveData<TokenEntity>()
tokenResponseFromApi?.observe(viewLifecycleOwner, Observer {
tokenResponseFromApi ->
if (tokenResponseFromApi != null) {
tokenFromApi = viewModel.convertTokenResponseToEntity(tokenResponseFromApi, dh.asDate)
}
})
tokenFromApi?.observe(viewLifecycleOwner, Observer {
tokenFromApi ->
if (tokenFromApi != null) {
viewModel.saveTokenToDB(repo, tokenFromApi)
}
})
}
Your problem is that you're registering the observer on tokenFromApi
during setup, and when you get your API response, you're replacing tokenFromApi
without registering an observer on it. So if it ever emits a value, you'll never know about it. The only observer you have registered is the one on the discarded tokenFromApi
which is never used by anything
Honestly your setup here isn't how you're supposed to use LiveData
. Instead of creating a whole new tokenFromApi
for each response, you'd just have a single LiveData
that things can observe. When there's a new value (like an API token) you set that on the LiveData
, and all the observers see it and react to it. Once that's wired up, it's done and it all works.
The way you're doing it right now, you have a data source that needs to be taken apart, replaced with a new one, and then everything reconnected to it - every time there's a new piece of data, if you see what I mean.
Ideally the Fragment is the UI, so it reacts to events (by observing a data source like a LiveData
and pushes UI events to the view model (someone clicked this thing, etc). That API fetching and DB storing really belongs in the VM - and you're already half doing that with those functions in the VM you're calling here, right? The LiveData
s belong in the VM because they're a source of data about what's going on inside the VM, and the rest of the app - they expose info the UI needs to react to. Having the LiveData
instances in your fragment and trying to wire them up when something happens is part of your problem
Have a look at the App Architecture guide (that's the UI Layer page but it's worth being familiar with the rest), but this is a basic sketch of how I'd do it:
class SomeViewModel ViewModel() {
// private mutable version, public immutable version
private val _tokenFromApi = MutableLiveData<TokenEntity>()
val tokenFromApi: LiveData<TokenEntity> get() = _tokenFromApi
fun callApi() {
// Do your API call here
// Whatever callback/observer function you're using, do this
// with the result:
result?.let { reponse ->
convertTokenResponseToEntity(response, dh.asDate)
}?.let { token ->
saveTokenToDb(repo, token)
_tokenFromApi.setValue(token)
}
}
private fun convertTokenResponseToEntity(response: String, date: Date): TokenEntity? {
// whatever goes on in here
}
private fun saveTokenToDb(repo: Repository, token: TokenEntity) {
// whatever goes on in here too
}
}
so it's basically all contained within the VM - the UI stuff like fragments doesn't need to know anything about API calls, whether something is being stored, how it's being stored. The VM can update one of its exposed LiveData
objects when it needs to emit some new data, update some state, or whatever - stuff that's interesting to things outside the VM, not its internal workings. The Fragment just observe
s whichever one it's interested in, and updates the UI as required.
(I know the callback situation might be more complex than that, like saving to the DB might involve a Flow
or something. But the idea is the same - in its callback/result function, push a value to your LiveData
as appropriate so observers can receive it. And there's nothing wrong with using LiveData
or Flow
objects inside the VM, and wiring those up so a new TokenEntity
gets pushed to an observer that calls saveTokenToDb
, if that kind of pipeline setup makes sense! But keep that stuff private if the outside world doesn't need to know about those intermediate steps