Android Dev Tip #4

The performance of wrapping content in RecyclerView

Tip:

Explanation:

The basic idea of a RecyclerView is very simple.

  1. You have a scrollable area filled with item Views.

2. When an item is scrolled off from the visible area, RecyclerView takes it out of the component.

3. The taken out item, can be used to show another item View that comes to the visible area of the scroll.

The whole thing becomes substantially less intuitive when you you want the RecyclerView to wrap its contents. In that case the size of the RecyclerView becomes the size of all item Views. As a result, RecyclerView itself is not scrollable anymore and no items can be scrolled off from the RecyclerView’s visible area. RecyclerView sees its whole area as a visible area.

All this leads to a simple fact: if no items can be scrolled off, then no items can be reused. All of them need to be available at all times.

All item Views are kept in the memory as long as your RecyclerView is in the layout hierarchy.

If that still doesn’t raise red flags for you, let me put it this way: if your RecyclerView displays an item for every single row of your database table, then if this table has 726 rows, you will end up having 726 item Views loaded in your memory at once.

Essentially, you end up with a component that can create a number of Views using an Adapter from some sort of data set. But the performance of the Views is the same as you would just manually add all the views in a ScrollView. No performance gain from the RecyclerView whatsoever.

Example:

Assuming the layout:

<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/test_image"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#3F51B5"
android:text="Example"/>

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>

And items being inflated in the RecyclerView:

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

If the Adapter for the RecyclerView has 30 records in its dataset, the screen could look (and work) like this:

At first sight everything looks good, but if you look at the Layout hierarchy (in Layout Inspector tool) you can see:

Every single item View from the RecyclerView is kept in memory at all times. Including multiple of item Views that are not currently visible (layout hierarchy capture taken with the NestedScrollView scrolled all the way to the top).

You can compare that to an equivalent case when there is no NestedScrollView and the RecyclerView is not wrapping its contents (same dataset):

At any point in time, the only Views kept in memory represent items currently within the Scroll’s bounds (of the RecyclerView).

If you enjoyed this post, please show your support! Recommend, follow, comment, share.

This really means a lot!

senior android engineer @reddit | former android tech lead @getthefabulous | recovering feature creep 💉 | https://github.com/blipinsk