web-dev-qa-db-fra.com

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

Je demande des images au présentateur de 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 lorsque ViewHolder sera réutilisé. C'est facile.

Mais comment arrêter tout abonnement lorsque la vue est détruite? Je devrais aussi probablement annuler la référence du présentateur pour éviter une fuite de mémoire

48
Eugen Martynov

Je pense que la meilleure façon de le faire serait de:

  1. Conservez une référence subscription dans le SiteAdapter.ViewHolder
  2. unsubscribe l'objet subscription dans onBindViewHolder (il est appelé lorsque ViewHolder est réutilisé)
  3. Conservez l’objet CompositeSubscription dans votre adapter
  4. Utilisez la méthode onDetachedFromRecyclerView de votre Adapter à unsubscribe le compositeSubscription

Ainsi:

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
        }
    }
}
20
Bartek Lipinski

Pour les autres qui ont le même problème: viewDetachedFromWindow dans l'adaptateur est appelé uniquement lorsque l'adaptateur est défini sur null dans onPause (Activity, Fragment) ou onDetachFromWindow (Activity, Fragment).

recyclerview.setAdapter (null)

Ensuite, vous obtenez viewDetachedFromWindow (...) où vous pouvez publier vos états internes et vos abonnements. Je configurerais vos abonnements dans une liaison, assurez-vous que tous les abonnements réappliquent avant chaque appel de liaison, car une vue peut être recyclée.

Une autre possibilité consiste à gonfler une vue personnalisée au lieu d'une seule disposition dans votre usine. Ensuite, vous pouvez effectuer le nettoyage dans la vue personnalisée onDetachFromWindow (). Vous obtenez également onDetachedFromWindow sans définir l'adaptateur sur null.

2
eugstman