RecyclerView适配器的生命周期是什么?

时间:2021-07-23 06:55:28

I'm requesting images from presenter in adapter:

我正在从适配器中的演示者请求图像:

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

I should unsubscribe when ViewHolder is re-used. It is easy.

我应该在重新使用ViewHolder时取消订阅。这很容易。

But how stop all subscription when view is destroyed? I should also probably nullify presenter reference to avoid memory leak

但是当视图被销毁时如何停止所有订阅?我也应该使presenter引用无效以避免内存泄漏

2 个解决方案

#1


17  

I think the best way to do that would be to:

我认为最好的办法是:

  1. Keep a subscription reference in the SiteAdapter.ViewHolder
  2. 在SiteAdapter.ViewHolder中保留订阅引用
  3. unsubscribe the subscription object in onBindViewHolder (it's called when the ViewHolder is reused)
  4. 取消订阅onBindViewHolder中的订阅对象(在重用ViewHolder时调用它)
  5. Keep the CompositeSubscription object in your adapter
  6. 将CompositeSubscription对象保留在适配器中
  7. Use the onDetachedFromRecyclerView method of your Adapter to unsubscribe the compositeSubscription
  8. 使用适配器的onDetachedFromRecyclerView方法取消订阅compositeSubscription

Like so:

像这样:

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


0  

For others which have the same problem: viewDetachedFromWindow in the adapter is only called when the adapter is set to null in the onPause (Activity, Fragment) or onDetachFromWindow (Activity, Fragment)

对于具有相同问题的其他问题:只有在onPause(Activity,Fragment)或onDetachFromWindow(Activity,Fragment)中将适配器设置为null时,才会调用适配器中的viewDetachedFromWindow

recyclerview.setAdapter(null)

recyclerview.setAdapter(空)

Then you get viewDetachedFromWindow(...) where you can release your internal states and subscriptions. I would setup your subscriptions in on bind, make sure before every bind call you relase old subscriptions as a view can be recycled.

然后,您将获得viewDetachedFromWindow(...),您可以在其中释放内部状态和订阅。我将在on bind中设置您的订阅,确保在每次绑定调用之前您重新关联旧订阅,因为视图可以被回收。

Another possibility is to inflate a custom view instead of only a layout in your factory. Then you can make the cleanup in the custom view onDetachFromWindow(). You get the onDetachedFromWindow also without setting the adapter to null.

另一种可能性是在工厂中扩展自定义视图而不仅仅是布局。然后,您可以在自定义视图onDetachFromWindow()中进行清理。您也可以在不将适配器设置为null的情况下获得onDetachedFromWindow。

#1


17  

I think the best way to do that would be to:

我认为最好的办法是:

  1. Keep a subscription reference in the SiteAdapter.ViewHolder
  2. 在SiteAdapter.ViewHolder中保留订阅引用
  3. unsubscribe the subscription object in onBindViewHolder (it's called when the ViewHolder is reused)
  4. 取消订阅onBindViewHolder中的订阅对象(在重用ViewHolder时调用它)
  5. Keep the CompositeSubscription object in your adapter
  6. 将CompositeSubscription对象保留在适配器中
  7. Use the onDetachedFromRecyclerView method of your Adapter to unsubscribe the compositeSubscription
  8. 使用适配器的onDetachedFromRecyclerView方法取消订阅compositeSubscription

Like so:

像这样:

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


0  

For others which have the same problem: viewDetachedFromWindow in the adapter is only called when the adapter is set to null in the onPause (Activity, Fragment) or onDetachFromWindow (Activity, Fragment)

对于具有相同问题的其他问题:只有在onPause(Activity,Fragment)或onDetachFromWindow(Activity,Fragment)中将适配器设置为null时,才会调用适配器中的viewDetachedFromWindow

recyclerview.setAdapter(null)

recyclerview.setAdapter(空)

Then you get viewDetachedFromWindow(...) where you can release your internal states and subscriptions. I would setup your subscriptions in on bind, make sure before every bind call you relase old subscriptions as a view can be recycled.

然后,您将获得viewDetachedFromWindow(...),您可以在其中释放内部状态和订阅。我将在on bind中设置您的订阅,确保在每次绑定调用之前您重新关联旧订阅,因为视图可以被回收。

Another possibility is to inflate a custom view instead of only a layout in your factory. Then you can make the cleanup in the custom view onDetachFromWindow(). You get the onDetachedFromWindow also without setting the adapter to null.

另一种可能性是在工厂中扩展自定义视图而不仅仅是布局。然后,您可以在自定义视图onDetachFromWindow()中进行清理。您也可以在不将适配器设置为null的情况下获得onDetachedFromWindow。