Home » Android » android – NotifyDataSetChanged- RecyclerView -Is it an asynchronous call?

android – NotifyDataSetChanged- RecyclerView -Is it an asynchronous call?

Posted by: admin May 14, 2020 Leave a comment

Questions:

I am trying to follow a set of statements after the execution of notifyDataSetChanged on a recyclerview. But when I am debugging my application, the debugger reaches the next few lines after my notifyDataSetChanged before going to the onBindViewHolder of the recyclerview’s adapter. So my question is- Is the notifyDataSetChanged an asynchronous call? If yes do we get a callback?
PS: I have already googled this answer and I couldn’t find a suitable answer that’s why I am asking the community.

How to&Answers:

RecyclerView author here,

When you call notifyDataSetChanged, RecyclerView invalidates the data but does not update the UI until the next animation frame. This is how android view system works. When a widget is invalidated (e.g. changing its data) it requests a layout which means it will be re-measured and re-laid out in the next view traversal. This is done so that we can batch all changes until the next time screen will be updated. This is why notifyDataSetChange does not trigger an onBind instantly.

So yes, you can call this as an async call but that does not mean that you can run it multi-threaded (these are two totally different concepts). You still have to make all changes to your adapter on the main thread. When you change the adapter, you have to notify RecyclerView instantly, which is why notify also has to be on the main thread.

The reason for this limitation is that if the data set is changed during a layout, it is very hard for the layout manager to recover to a stable state (e.g. imagine RecyclerView calls onBind(5) and item 5 is removed in another thread at the same time). Also, accounting for such changes will require a lot of synchronization which will be a big performance penalty without any benefit. This is why all UI components are single threaded.

Answer:

TLDR: No. You can call notifyDatasetChanged() only from the UI thread as indicated here which does the changes to the UI which can only be done on the main thread.This makes the call synchronous.

This call is meant to redraw the ui with the changed elements.It is like calling requestFocus() on a view.

From the documentation:

RecyclerView will attempt to synthesize visible structural change events for adapters that report that they have stable IDs when this method is used. This can help for the purposes of animation and visual object persistence but individual item views will still need to be rebound and relaid out.

Since the views are redrawn because the dataset expects a change,It has to be on the main UI thread(only the main looper(i.e the UI thread) can modify views).

You can have multiple AdapterObservers which can perform actions that you need on the ui.

Also from the documentation:

  • This event does not specify what about the data set has changed, forcing
    * any observers to assume that all existing items and structure may no longer be valid.
    * LayoutManagers will be forced to fully rebind and relayout all visible views.

The default observer will assume that all the data has been changed since from the source code of Recycler view :

 public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }

It is a synchronous call which should be avoided and used as a last resort.

Answer:

I came across this issue because I needed to do some layout reconfiguration based on the size of my recyclerview, and since I had height set to wrap_content, the size was changing whenever its data would change. To effectively respond to layout changes, you can use a short subclass of your layout manager (in my case it was a LinearLayoutManager) and override onLayoutCompleted:

recyclerView.setLayoutManager(new LinearLayoutManager(getContext()){
        @Override
        public void onLayoutCompleted (RecyclerView.State state){
            super.onLayoutCompleted(state);
            // update UI for new layout
        }
    });

In my case I needed to scroll my ScrollView after the size changed on the RecyclerView, and this code did the trick.