web-dev-qa-db-fra.com

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

Votre premier ne fonctionnera pas, car observeForever() n'est lié à aucune 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 un LifecycleOwner (votre activité) ou non. Mon hypothèse est que vous devriez utiliser un 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);
17
CommonsWare

À la suite de CommonsWare answer, au lieu d’appeler removeObservers() qui supprimera tous les observateurs attachés à LiveData, vous pouvez simplement appeler removeObserver(this) pour ne supprimer que 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: dans removeObserver(this), this fait référence à l'instance observateur et cela ne fonctionne que dans le cas d'une classe interne anonyme. Si vous utilisez un lambda, alors this fera référence à l'instance d'activité.

8
Toni Joe

Il existe une solution plus pratique pour Kotlin avec des extensions:

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

Cette extension nous permet de faire cela:

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

Donc, pour répondre à votre question initiale, 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 originale est ici: https://code.luasoftware.com/tutorials/Android/android-livedata-observe-once-only-kotlin/

1
Vince

Je suis d'accord avec @vince ci-dessus, mais je pense que nous pouvons soit ignorer passer 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 lifecycleOwner avec observe comme 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)
        }
    })
}
0
Hakem Zaied
  1. La classe LiveData a 2 méthodes similaires pour supprimer des observateurs. Le premier est nommé, 

removeObserver(@NonNull final Observer<T> observer) (voir attentivement le nom de la méthode, c'est le singulier) qui prend l'observateur que vous souhaitez supprimer de la liste des observateurs du même LifecycleOwner. 

  1. Deuxième méthode est 

removeObservers(@NonNull final LifecycleOwner owner) (voir le nom de la méthode plurielle). Cette méthode prend en compte le LifecycleOwner lui-même et supprime tous les observateurs du LifecycleOwner spécifié.

Maintenant, dans votre cas, vous pouvez supprimer votre Observateur de 2 manières (il peut y en avoir de plusieurs façons), l'une d'entre elles est racontée par @ToniJoe dans la réponse précédente.

Une autre façon consiste simplement à avoir un MutableLiveData de booléen dans votre ViewModel, qui stocke true quand il a été observé pour la première fois et observez également ce Livedata. Ainsi, chaque fois que cela devient vrai, vous en serez averti et vous pourrez alors retirer votre observateur en le faisant passer.

0
Abhishek Kumar