Android scroll position restoring done right

For some reason there are a lot of quite misleading tips/suggestions on how to save and restore scroll position in your_scrolling_container upon orientation changes.

  • Taking current scroll position and saving it in Activity’s onSaveInstanceState
  • Extending a certain scrollable View to do same there
  • Preventing Activity from being destroyed on rotation

And yeah, they are working fine, but…

But in fact, everything is much simpler, because Android is already doing it for you!

If you take a closer look at
RecyclerView/ListView/ScrollView/NestedScrollView sources, you’ll see that each of them is saving its scroll position in onSaveInstanceState. And during the first layout pass they are trying to scroll to this position in onLayout method.

There are only 2 things you need to do, to make sure it’s gonna work fine:

  1. Set an id for your scrollable view, which is probably already done. Otherwise Android won’t be able to save View state automatically.
  2. Provide a data before the first layout pass, to have the same scroll boundaries you had before rotation. That’s the step where developers usually have some issues.

So if you are loading your data asynchronously on each screen rotation and then posting it to the Main thread through the Looper queue, you will always end up with losing the previous scroll position. In fact, any

handler.post(this::setListItems);

will break it as well, because this action will be posted into the queue and delivered after the list has already started its layout.

The right way is to load data once, cache it, and set it in a blocking manner on rotation, without any handler interaction.

It requires your cache to survive orientation changes, of course. It means that you need to keep it in something, that lives longer than Activity (ViewModels, Loaders, Retained Fragments, lastCustomNonConfigurationInstance, Application, etc).

By the way, you may like Loaders or not, but you probably noticed, that a long time ago, when you were using a CursorLoader (which is loading stuff asynchronously) to show some list data, you never had problems with preserving scroll position on rotation. And there’s no magic. The only thing it does, is checking for a cache and delivering it instantly in the Main thread.

But that’s completely different story, it’s up to you how to design a cache. 
The main idea is that you need to deliver data properly to not bother with manual position preserving.