LiveData remove Observer after first callback
How do I remove the observer after I receive the first result? Below are two code ways I've tried, but they both keep receiving updates even though I have removed the observer.
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observeForever(observer);
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, downloadItem-> {
if(downloadItem!= null) {
this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(downloadItem-> {});
} );
Solution 1:
There is a more convenient solution for Kotlin with extensions:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
This extension permit us to do that:
liveData.observeOnce(this, Observer<Password> {
if (it != null) {
// do something
}
})
So to answer your original question, we can do that:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
}
startDownload();
})
The original source is here: https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/
Update: @Hakem-Zaied is right, we need to use observe
instead of observeForever
.
Solution 2:
Your first one will not work, because observeForever()
is not tied to any LifecycleOwner
.
Your second one will not work, because you are not passing the existing registered observer to removeObserver()
.
You first need to settle on whether you are using LiveData
with a LifecycleOwner
(your activity) or not. My assumption is that you should be using a LifecycleOwner
. In that case, use:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObservers((AppCompatActivity)context);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
Solution 3:
Following on CommonsWare answer, instead of calling removeObservers()
which will remove all the observers attached to the LiveData, you can simply call removeObserver(this)
to only remove this observer:
Observer observer = new Observer<DownloadItem>() {
@Override
public void onChanged(@Nullable DownloadItem downloadItem) {
if(downloadItem!= null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
return;
}
startDownload();
model.getDownloadByContentId(contentId).removeObserver(this);
}
};
model.getDownloadByContentId(contentId).observe((AppCompatActivity)context, observer);
Note: in removeObserver(this)
, this
refers to the observer instance and this works only in the case of an anonymous inner class. If you use a lambda, then this
will refer to the activity instance.