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

  1. Класс LiveData имеет 2 аналогичных метода для удаления Observers. Первый назван,

removeObserver(@NonNull final Observer<T> observer) (внимательно посмотрите название метода, оно единственное число), который берет наблюдателя, которого вы хотите удалить из списка наблюдателей того же LifecycleOwner.

  1. Второй метод

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() вызывается первым делом при отправке наблюдателя, что делает его более безопасным (т.е. справляется с потенциальными ошибками во время выполнения изнутри кода пользовательского наблюдателя).