web-dev-qa-db-ja.com

RecyclerViewアダプターのライフサイクルとは何ですか?

アダプタのプレゼンターに画像を要求しています:

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

ViewHolderが再利用されたときに登録を解除する必要があります。簡単です。

しかし、ビューが破棄されたときにすべてのサブスクリプションを停止する方法は?また、おそらくメモリリークを避けるためにプレゼンターの参照を無効にする必要があります

48
Eugen Martynov

そのための最善の方法は次のとおりだと思います。

  1. SiteAdapter.ViewHoldersubscription参照を保持します
  2. unsubscribesubscription内のonBindViewHolderオブジェクト(ViewHolderが再利用されるときに呼び出されます)
  3. CompositeSubscriptionオブジェクトをadapterに保持します
  4. onDetachedFromRecyclerViewAdapterメソッドをunsubscribeからcompositeSubscriptionに使用します

そのようです:

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

同じ問題がある他の場合:アダプタのviewDetachedFromWindowは、アダプタがonPause(アクティビティ、フラグメント)またはonDetachFromWindow(アクティビティ、フラグメント)でnullに設定されている場合にのみ呼び出されます。

recyclerview.setAdapter(null)

次に、viewDetachedFromWindow(...)を取得して、内部状態とサブスクリプションを解放できます。バインド時にサブスクリプションを設定します。ビューをリサイクルできるように、すべてのバインド呼び出しの前に古いサブスクリプションを解放するようにしてください。

別の可能性は、工場内のレイアウトだけではなく、カスタムビューを膨らませることです。次に、カスタムビューonDetachFromWindow()でクリーンアップを実行できます。アダプタをnullに設定せずにonDetachedFromWindowも取得します。

2
eugstman