2

I have a feed when clicking on item go to details, during it FeedFragment is detached and DetailsFragment pushed. When going back we're checking if an adapter is not null.

    if (adapter == null) {
      adapter = createAdapter();
    }

    if (recyclerView.getAdapter() == null) {
      recyclerView.setAdapter(adapter);
    }

The problem is when reusing adapter every time going to details and going back the amount of ViewHolders is doubled. And it can go on until it will freeze the app and OOM happens.

The question is why is this happening? Shouldn't adapter be detached from recyclerView and all items should be recycled?

P.S. this problem doesn't occur when creating new adapter when going back.

ar-g
  • 3,417
  • 2
  • 28
  • 39
  • Are you sure onDetach call back is received? And *amount of ViewHolders is doubled*, for this you need to have clearAdaptor function inside the adaptor which the clear the data passed into the adaptor and also notifies data set changed. – Sanoop Surendran Jun 20 '17 at 09:06
  • Post `createAdapter()` code. – azizbekian Jun 20 '17 at 09:19
  • @Sanoop I'm doing `public abstract FragmentTransaction detach(Fragment fragment);` but onDetach is not happening on a fragment. Yes it doubles, I investigated heap specifically with this purpose – ar-g Jun 20 '17 at 09:32
  • Did you checked [this](https://stackoverflow.com/a/26421792/5733111) also the comment in it. – Sanoop Surendran Jun 20 '17 at 09:34
  • yes, the fragment behavior may cause this also – ar-g Jun 20 '17 at 09:45

2 Answers2

4

I think that you are leaking RecyclerViews (and everything they references, including the view pools)

A RecyclerView registers itself on the Adapter (Adapter.registerAdapterDataObserver) but it unregisters only when a new adapter is set (it unregisters from the old and registers on the new). So the adapter keeps references of the previous RecyclerView instances, preventing them to be garbage collected.

The solution I can think of are :

  • Unregister manually the RecyclerView from the adapter in Fragment.onDestroyView(). The only way I know to do this is to call RecyclerView.setAdapter(null)

  • Use a new adapter each time. It is supposed to be relatively lightweight object, at least compared to the RecyclerView itself.

Additionaly, if you want to avoid recreating a set of ViewHolders each time, it is may be possible to share a RecycledViewPool between RecyclerView instances. (I just discovered this, I am not sure how it is supposed to be used)

bwt
  • 17,292
  • 1
  • 42
  • 60
0

The solution i am suggestion is like this, you should have a clear adaptor function inside the adaptor class,

public void clearAdaptor() {
    if (<T>dataList != null && <T>dataList.size() > 0) {
        <T>dataList.clear();
        notifyDataSetChanged();
    }
}

And you need to call this function after initially setting the adaptor.

    adaptorObj = new Adaptor(/**Your implementation**/);
    mRecyclerView.setAdapter(adaptorObj);
    adaptorObj.clearAdaptor();

    getYourData();

after getting the data, call

   adaptorObj.notifyDataSetChanged();

PS: This is a working solution if the data is dynamic, but can also be modified if the data is static.

Sanoop Surendran
  • 3,484
  • 4
  • 28
  • 49
  • Now I'm doing it this way in adapter `@Override public void set(Collection extends T> elements) { clear(); addAll(elements); notifyDataSetChanged(); }` – ar-g Jun 20 '17 at 09:34
  • Clearing adapter, as you suggested, didn't help – ar-g Jun 20 '17 at 09:39
  • I will probably stick to creating a new adapter for now. But I'm very interested why is this happening. Also, I'm noticed that `onDetachedFromRecyclerView` not being called as well when detaching fragment. – ar-g Jun 20 '17 at 09:43