Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 566 Vote(s) - 3.47 Average
  • 1
  • 2
  • 3
  • 4
  • 5
is observeForever lifecycle aware?

#1
I'm working with MVVM, and I have made different implementations of it, but one thing that is still making me doubt is how do I get data from a Repository (Firebase) from my ViewModel without attaching any lifecycle to the ViewModel.

I have implemented `observeForever()` from the ViewModel, but I don't think that is a good idea because I think I should communicate from my repository to my ViewModel either with a callback or a Transformation.

I leave here an example where I fetch a device from Firebase and update my UI, if we can see here, I'm observing the data coming from the repo from the UI, but from the ViewModel I'm also observing data from the repo, and here is where I really doubt if I'm using the right approach, since I don't know if `observeForever()` will be cleared on `onCleared()` if my view is destroyed, so it won't keep the observer alive if the view dies.


UI
--

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
val deviceId = editText.text.toString().trim()
observeData(deviceId)
}
}

fun observeData(deviceId:String){
viewModel.fetchDeviceData(deviceId).observe(this, Observer {
textView.text = "Tipo: ${it.devType}"
})



ViewModel
---------

class MainViewmodel: ViewModel() {

private val repo = Repo()
fun fetchDeviceData(deviceId:String):LiveData<Device>{
val mutableData = MutableLiveData<Device>()
repo.getDeviceData(deviceId).observeForever {
mutableData.value = it
}

return mutableData
}
}


Repository
----------

class Repo {

private val db = FirebaseDatabase.getInstance().reference
fun getDeviceData(deviceId:String):LiveData<Device>{
val mutableData = MutableLiveData<Device>()
db.child(deviceId).child("config/device").addListenerForSingleValueEvent(object: ValueEventListener{

override fun onDataChange(dataSnapshot: DataSnapshot) {
val device = dataSnapshot.getValue(Device::class.java)
mutableData.value = device
}

override fun onCancelled(dataError: DatabaseError) {
Log.e("Error","handle error callback")
}
})

return mutableData
}
}


This example just shows how to fetch the device from Firebase, it works, but from my ViewModel, it keeps making me think that `observeForever()` is not what I'm looking for to communicate data between the repository to the ViewModel.

I have seen `Transformations`, but I, in this case, I just need to deliver the entire Device object to my UI, so I don't need to transform the Object I'm retrieving to another Object

What should be here the right approach to communicate the repository and the ViewModel properly?
Reply

#2
To use ObserveForever, you need to remove the observer inside onClear in the ViewModel.

In this case, I would suggest to use Transformation even though you just need a direct mapping without any processing of the data, which is actually the same as what you are doing with the observer for observerForever.
Reply

#3
`observeForever()` is not Lifecycle aware and will continue to run until ` removeObserver()` is called.
In your ViewModel do this instead,
```
class MainViewmodel: ViewModel() {

private val repo = Repo()
private var deviceData : LiveData<Device>? = null
fun fetchDeviceData(deviceId:String):LiveData<Device>{
deviceData = repo.getDeviceData(deviceId)
return deviceData!!
}
}
```
Reply

#4
> is observeForever lifecycle aware?


No, that's why it's called `observe`***Forever***.

> I have implemented observeForever() from the ViewModel, but I don't think that is a good idea

No, it's not, you should be using `Transformations.switchMap {`.

> since I don't know if observeForever() will be cleared on onCleared() if my view is destroyed, so it won't keep the observer alive if the view dies.

Well if ***you're*** not clearing it in `onCleared()` using `removeObserver(observer)`, then it won't clear itself, because it observes *forever*.


> here is where I really doubt if I'm using the right approach,

No, you can do much better than this following a reactive approach.

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

button.setOnClickListener {
val deviceId = editText.text.toString().trim()
viewModel.onSelectedDeviceChanged(deviceId)
}

viewModel.selectedDevice.observe(this, Observer { device ->
textView.text = "Tipo: ${device.devType}"
})
}

And

class MainViewModel(
private val savedStateHandle: SavedStateHandle,
): ViewModel() {
private val repo = Repo() // TODO: move to Constructor Argument with ViewModelProvider.Factory

private val selectedDeviceId: MutableLiveData<String> = savedStateHandle.getLiveData<String>("selectedDeviceId")

fun onSelectedDeviceChanged(deviceId: String) {
selectedDeviceId.value = deviceId
}

val selectedDevice = Transformations.switchMap(selectedDeviceId) { deviceId ->
repo.getDeviceData(deviceId)
}
}

And

class Repo {
private val db = FirebaseDatabase.getInstance().reference // TODO: move to constructor arg? Probably

fun getDeviceData(deviceId:String) : LiveData<Device> {
return object: MutableLiveData<Device>() {
private val mutableLiveData = this

private var query: Query? = null
private val listener: ValueEventListener = object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val device = dataSnapshot.getValue(Device::class.java)
mutableLiveData.value = device
}

override fun onCancelled(dataError: DatabaseError) {
Log.e("Error","handle error callback")
}
}

override fun onActive() {
query?.removeEventListener(listener)
val query = db.child(deviceId).child("config/device")
this.query = query
query.addValueEventListener(listener)
}

override fun onInactive() {
query?.removeEventListener(listener)
query = null
}
}
}
}

This way, you can observe for changes made in Firebase (and therefore be notified of future changes made to your values) using LiveData, rather than only execute a single fetch and then not be aware of changes made elsewhere to the same data.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through