71 votes

Quel est le cycle de vie de l'adaptateur RecyclerView ?

Je demande des images au présentateur dans l'adaptateur :

  @Override
  public void onBindViewHolder(SiteAdapter.ViewHolder holder, int position)
  {
    Site site = sites.get(position);
    holder.siteName.setText(site.getName());
    requestHolderLogo(holder, site.getLinks().getLogoUrl());
  }

  private void requestHolderLogo(final ViewHolder holder, final String logoUrl)
  {
    compositeSubscription.add(
      presenter.bitmap(logoUrl)
        .subscribe(
          bitmap -> {
            holder.siteLogo.setImageBitmap(bitmap);
            holder.siteLogo.setVisibility(View.VISIBLE);
          },
          error -> {
            holder.siteName.setVisibility(View.VISIBLE);
          })
    );
  }

Je devrais me désabonner quand ViewHolder est réutilisé. C'est facile.

Mais comment arrêter tous les abonnements lorsque la vue est détruite ? Je devrais aussi probablement annuler la référence au présentateur pour éviter les fuites de mémoire.

20 votes

Veuillez expliquer pourquoi le downvoting

0 votes

Vérifier ma réponse ici stackoverflow.com/a/68351804/6039240

31voto

blipinsk Points 467

Je pense que le meilleur moyen de le faire serait de :

  1. Gardez un subscription référence dans la SiteAdapter.ViewHolder
  2. unsubscribe le site subscription objet dans onBindViewHolder (il est appelé lorsque le ViewHolder est réutilisé)
  3. Gardez le CompositeSubscription dans votre adapter
  4. Utilisez le onDetachedFromRecyclerView de votre Adapter à unsubscribe le site compositeSubscription

Comme ça :

public class SiteAdapter extends RecyclerView.Adapter<SiteAdapter.ViewHolder> {

    private CompositeSubscription compositeSubscription = new CompositeSubscription();

    // other needed SiteAdapter methods

    @Override
    public void onBindViewHolder(SiteAdapter.ViewHolder holder, int position) {
        if (holder.subscription != null && !holder.subscription.isUnsubscribed()) {
            compositeSubscription.remove(holder.subscription);
            // this will unsubscribe the subscription as well
        }
        Site site = sites.get(position);
        holder.siteName.setText(site.getName());
        requestHolderLogo(holder, site.getLinks().getLogoUrl());
    }

    private void requestHolderLogo(final SiteAdapter.ViewHolder holder, final String logoUrl) {
        holder.subscription = presenter.bitmap(logoUrl)
                .subscribe(
                        bitmap -> {
                            holder.siteLogo.setImageBitmap(bitmap);
                            holder.siteLogo.setVisibility(View.VISIBLE);
                        },
                        error -> {
                            holder.siteName.setVisibility(View.VISIBLE);
                        });
        compositeSubscription.add(holder.subscription);
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        compositeSubscription.unsubscribe();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public Subscription subscription;

        // some holder-related stuff

        public ViewHolder(View itemView) {
            super(itemView);
            // init holder
        }
    }
}

2 votes

Cool, donc il n'y a pas de méthode sur l'adaptateur qui est appelée quand RecyclerView est détaché de l'activité

0 votes

Eh bien... en fait, il y en a un - onDetachedFromRecyclerView et il pourrait facilement faire ce que le recycle() le fait actuellement. Bonne idée :)

8 votes

onDetachedFromRecyclerView est appelé lorsque vous changez d'adaptateur, mais il n'est pas appelé lorsque la configuration a changé :/

6voto

eugstman Points 66

Pour les autres qui ont le même problème : viewDetachedFromWindow dans l'adaptateur n'est appelé que lorsque l'adaptateur est mis à null dans le onPause (Activity, Fragment) ou le onDetachFromWindow (Activity, Fragment).

recyclerview.setAdapter(null)

Ensuite, vous obtenez viewDetachedFromWindow(...) où vous pouvez libérer vos états internes et vos abonnements. Je configurerais vos abonnements dans un bind, assurez-vous avant chaque appel bind que vous relâchez les anciens abonnements car une vue peut être recyclée.

Une autre possibilité est de gonfler une vue personnalisée au lieu d'un simple layout dans votre factory. Vous pouvez alors effectuer le nettoyage dans la vue personnalisée onDetachFromWindow(). Vous obtenez le onDetachedFromWindow également sans mettre l'adaptateur à null.

0 votes

Comment allez-vous résoudre le même problème si l'abonnement est quelque chose qui continue à écouter pour plus de données ? Pour le contexte, j'utilise l'écouteur de changement de données de Firebase.

0 votes

@Adi Je créerais une vue personnalisée et j'utiliserais onDetachFromWindow().

0 votes

Je pense que cela ne résoudrait pas le problème. onDetachFromWindow est appelé lorsque l'activité est active et que la vue du recycleur est sortie de la fenêtre. Mais si vous fermez une activité, alors cette méthode n'est pas appelée pour les dernières vues visibles sur la fenêtre. Il y aurait donc une fuite de mémoire.

3voto

Susanta Points 53

On peut appeler public void onViewRecycled(@NonNull VH holder) enter image description here

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