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.