117 votes

LiveData enlève Observer après le premier rappel

Comment puis-je retirer l'observateur après avoir reçu le premier résultat? J'ai essayé ci-dessous deux méthodes de code, mais les deux continuent à recevoir des mises à jour même si j'ai supprimé l'observateur.

 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-> {});
        } );
 

112voto

Vince Points 502

Il est plus commode solution pour Kotlin avec les 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)
        }
    })
}

Cette extension nous permet de le faire:

liveData.observeOnce(this, Observer<Password> {
    if (it != null) {
        // do something
    }
})

Donc, pour répondre à votre question de départ, nous pouvons le faire:

val livedata = model.getDownloadByContentId(contentId)
livedata.observeOnce((AppCompatActivity) context, Observer<T> {
    if (it != null) {
        DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists");
    }
    startDownload();
})

La source d'origine est ici: https://code.luasoftware.com/tutorials/android/android-livedata-observe-once-only-kotlin/

Mise à jour: @Hakim-Zaied est droit, nous avons besoin d'utiliser observe au lieu de observeForever.

61voto

CommonsWare Points 402670

Votre premier ne fonctionnera pas, car observeForever() n'est lié à aucun LifecycleOwner .

Votre deuxième ne fonctionnera pas, car vous ne transmettez pas l'observateur enregistré existant à removeObserver() .

Vous devez d’abord décider si vous utilisez LiveData avec LifecycleOwner (votre activité) ou non. Mon hypothèse est que vous devriez utiliser LifecycleOwner . Dans ce cas, utilisez:

 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);
 

41voto

und0 Points 6

J'aime les solutions génériques de @Vince et @Hakem Zaied, mais pour moi, la version lambda semble encore meilleure:

 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)
        }
    })
}
 

Donc vous vous retrouvez avec:

     val livedata = model.getDownloadByContentId(contentId)
    livedata.observeOnce((AppCompatActivity) context) {
        if (it != null) {
            DownloadManager.this.downloadManagerListener.onDownloadManagerFailed(null, "this item already exists")
        }
        startDownload();
    }
 

Que je trouve plus propre.

De plus, removeObserver() est appelé première chose lors de la répartition de l'observateur, ce qui le rend plus sûr (c'est-à-dire que les erreurs d'exécution potentielles sont générées depuis le code de l'observateur de l'utilisateur).

37voto

Toni Joe Points 1011

La suite sur CommonsWare réponse, au lieu de l'appeler removeObservers() qui va supprimer tous les observateurs attachés à la LiveData, vous pouvez simplement appeler removeObserver(this) seulement de supprimer cet observateur:

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);

Remarque: en removeObserver(this), this se réfère à l'observateur de l'instance et cela ne fonctionne que dans le cas d'un anonyme intérieur de la classe. Si vous utilisez un lambda, alors this va se référer à l'activité de l'instance.

18voto

Hakem Zaied Points 3766

Je suis d'accord avec @vince ci-dessus, mais je pense que nous pouvons soit ignorer les lifecycleOwner et utiliser observerForever comme ci-dessous:

 fun <T> LiveData<T>.observeOnce(observer: Observer<T>) {
    observeForever(object : Observer<T> {
        override fun onChanged(t: T?) {
            observer.onChanged(t)
            removeObserver(this)
        }
    })
}
 

ou en utilisant les lifecycleOwner avec observe ci-dessous:

 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)
        }
    })
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X