I am using an Observable field in a ViewModel. When the Observable field gets updated, I change the UI visibility.
This can be done either done by
object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
remove the callback in ondestroy.
or
directly mapping in XML like @{}
using two-way binding.
Now the question is how do I remove the listener if using two-way binding? I know the Livedata can be a replacement for this.
Stay organized with collections Save and categorize content based on your preferences. The @={} notation, which importantly includes the "=" sign, receives data changes to the property and listen to user updates at the same time.
An observable class that holds a parcelable object. A convenience class that implements Observable interface and provides notifyPropertyChanged(int) and notifyChange() methods.
LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
I am not sure regarding which memory leak you are talking.
Memory leak in Java occur when one object exists long period of time and it contains strong references to other objects that should not be used anymore, thus should be destroyed by GC, but still persist because of that strong reference.
In Android specifically memory leaks usually occur when some long lasting object stores strong reference to an Activity (or in some cases Fragment). All the other memory leaks in android are not so impactful(except the ones with bitmaps - but it is a completely different topic)
So let us return to the data binding with an ObservableField
and its callbacks inside the ViewModel
or two way data binding via @={}
. In most of the cases there will be no memory leak in both of those cases. To understand why - you will need to understand how does Android framework operates with UI and also understand now does view data binding works. So what happens when you are creating a callback via either ObservableField
and callback or with @={}
When you write
val someField: ObservabaleField = ObservableFiled<String>("someText")
val someCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
someField.addOnPropertyChangedCallback(someCallback)
// and in the layout
android:text="@={viewModel.someField}"
In the generated file it does something like this
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, viewModelSomeFieldGet);
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
//...
return onChangeViewModelSomeOtherStuff(object, fieldId);
case 1 :
return onChangeViewModelSomeField((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
As you can see there is no context
neither activity
or fragment
leaks since there is no strong reference to them stored anywhere. There is no references to context
, activity
or fragment
in your ViewModel
either(I hope!). Moreover it works the other way around - ui stores link to the ViewModel
in the binding implementation thus our ViewModel
may be leaking. It is rear case since the UI of an Activity or a Fragment usually gets destroyed along with its ActivityBindingImpl
or FragmentBindingImpl
bindings but...
To be sure you have manual way to clear references: in either Activity' onDestroy
or Fragment' onDestroyView
call
clearFindViewByIdCache()
binding.unbind()
binding = null
// if you store view link in your viewModel(which is bad and may cause leaks) this is the perfect place to nullify it
viewModel.view = null
Also to handle binding auto clearing you may use AutoClearedValue
the actual usage may look like(if you don't care about its type)
override var binding: ViewDataBinding? by autoCleared()// that is all - no need of onDestroy or onDestroyView
Edit
If you want to manually unregister all the callbacks from your ObservableField
s you can do it. The best way to do it is in onCleared()
method of ViewModel
. You should call observableField.removeOnPropertyChangedCallback(callback)
to handle the stuff. It will look like this considering ObservableField
and callback declarations above:
class MyViewModel: ViewModel{
//ObservableField and callback declarations
...
override void onCleared(){
someField.removeOnPropertyChangedCallback(someCallback)
}
}
Edit end
This all things I've just described ensures absence of memory leaks while using ObservableFields
and view data bindings. It is all about a correct implementation. Of course you can implement it with leaks, but you can implement it without ones.
Comment if something is still unclear - I will try to expand the answer.
A bit more info about Fragment dependent leaks here
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With