LiveData удаляет 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-> {});
} );
Ответы
Ответ 1
Ваш первый не будет работать, потому что observeForever()
не привязана ни к одному LifecycleOwner
.
Ваш второй не будет работать, потому что вы не передаете существующего зарегистрированного наблюдателя removeObserver()
.
Сначала вам нужно решить, используете ли вы LiveData
с LifecycleOwner
(ваша деятельность) или нет. Мое предположение заключается в том, что вы должны использовать LifecycleOwner
. В этом случае используйте:
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);
Ответ 2
После ответа CommonsWare вместо вызова removeObservers()
который удалит всех наблюдателей, подключенных к LiveData, вы можете просто вызвать removeObserver(this)
чтобы удалить этого наблюдателя:
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);
Примечание: в removeObserver(this)
this
относится к экземпляру наблюдателя, и это работает только в случае анонимного внутреннего класса. Если вы используете лямбда, то this
будет относиться к экземпляру активности.
Ответ 3
Для Kotlin есть более удобное решение с расширениями:
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)
}
})
}
Это расширение позволяет нам сделать это:
liveData.observeOnce(this, Observer<Password> {
if (it != null) {
// do something
}
})
Итак, чтобы ответить на ваш оригинальный вопрос, мы можем сделать это:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
}
startDownload();
})
Первоначальный источник находится здесь: https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/
Обновление: @Хакем-Зайед прав, нам нужно использовать observe
вместо observeForever
.
Ответ 4
Я согласен с @vince выше, но я считаю, что мы либо пропускаем передачу lifecycleOwner
и используем observerForever
как показано ниже:
fun <T> LiveData<T>.observeOnce(observer: Observer<T>) {
observeForever(object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
или используя lifecycleOwner
observe
ниже:
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)
}
})
}
Ответ 5
- Класс LiveData имеет 2 аналогичных метода для удаления Observers. Первый назван,
removeObserver(@NonNull final Observer<T> observer)
(внимательно посмотрите название метода, оно единственное число), который берет наблюдателя, которого вы хотите удалить из списка наблюдателей того же LifecycleOwner.
- Второй метод
removeObservers(@NonNull final LifecycleOwner owner)
(см. имя метода во множественном числе). Этот метод принимает сам LifecycleOwner и удаляет все наблюдатели указанного LifecycleOwner.
Теперь в вашем случае вы можете удалить Observer двумя способами (может быть много), один из них сказал @ToniJoe в предыдущем ответе.
Другой способ - просто иметь MutableLiveData логического значения в вашей ViewModel, который хранит значение true, когда оно было замечено в первый раз, и просто наблюдать за этими Livedata. Поэтому, когда бы оно ни становилось истинным, вы будете уведомлены, и там вы сможете удалить своего наблюдателя, передав этого конкретного наблюдателя.
Ответ 6
Мне нравятся общие решения @Vince и @Hakem Zaied, но мне кажется, что лямбда-версия еще лучше:
fun <T> LiveData<T>.observeOnce(observer: (T) -> Unit) {
observeForever(object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
fun <T> LiveData<T>.observeOnce(owner: LifecycleOwner, observer: (T) -> Unit) {
observe(owner, object: Observer<T> {
override fun onChanged(value: T) {
removeObserver(this)
observer(value)
}
})
}
Итак, вы в конечном итоге:
val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context) {
if (it != null) {
DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
}
startDownload();
}
Который я нахожу чище.
Кроме того, removeObserver()
вызывается первым делом при отправке наблюдателя, что делает его более безопасным (т.е. справляется с потенциальными ошибками во время выполнения изнутри кода пользовательского наблюдателя).