'java.lang.IllegalStateException:Make sure the content of your adapter is not modified from a background thread, but only from the UI thread

  java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296513, class xyz.ScrollDetectableListView) with Adapter(class android.widget.HeaderViewListAdapter)]

I am getting above exception sometimes while scrolling through the dynamic listview and then clicking on item.I researched a lot but unable to find the exact reason that why i am getting this error sometimes and how it can be resolved?

     private ScrollDetectableListView mFListView;

    public FAdapter mFAdapter;    

         @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
         View rootView = inflater.inflate(R.layout.fragment_feed_view, container, false);

                View headerView = getActivity().getLayoutInflater().inflate(R.layout.view_feed_header, null);
                try{
                mFListView = (ScrollDetectableListView) rootView.findViewById(R.id.feed_list_view);

                mFContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.feed_container);
                mFListView.addHeaderView(headerView);
                mFListView.setEmptyView(rootView.findViewById(R.id.empty_view));
                mFContainer.setColorSchemeResources(R.color.green, R.color.pink, R.color.fbcolor,
                        R.color.instagramcolor, R.color.googlecolor, R.color.flickrcolor);


                mFView = getActivity().getLayoutInflater().inflate(R.layout.view_footer, null);
                ImageView rotateImageView = (ImageView) mFooterView.findViewById(R.id.spinner);
                Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate);
                rotation.setFillAfter(false);
                rotateImageView.startAnimation(rotation);

                mFContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                    @Override
                    public void onRefresh()
                    {
                        initializeFListView();
                    }
                });

                initializeFListView();
                mProgressDialog.setVisibility(View.VISIBLE);

                mHActivity.setDataChangedListener(new DataChangedListener() {
                    @Override
                    public void onDataChanged() {

                        mFContainer.setRefreshing(true);
                        mProgressDialog.setVisibility(View.GONE);
                        initializeFListView();
                    }
                });
     }catch(Exception e){}
            return rootView;
        }

 public void initializeFListView()
    {
  FApi.getTrending(getActivity(), xyz, new APIResponseListener() {
                @Override
                public void onResponse(Object response) {

                    setFeedAdapter((List<Video>) response);
                }

                @Override
                public void onError(VolleyError error) {

                    if (error instanceof NoConnectionError) {
                        String errormsg = getResources().getString(R.string.no_internet_error_msg);
                        Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                    }

                }
            });
}
        private void setFAdapter(List<Video> response)
        {try {

            List<Video> videos = response;

            mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
            mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
            mFListView.setAdapter(mFAdapter);
            mProgressDialog.setVisibility(View.GONE);
            if (mFContainer.isRefreshing()) {
                mFContainer.setRefreshing(false);
            }
            if (mFAdapter.getCount() < mCount) {
                mFView.setVisibility(View.GONE);
                mFListView.removeFooterView(mFooterView);
            }
        }catch(Exception e){}
        }

    }


Solution 1:[1]

My suggestion try to set ur list adapter on UI Thread,,,

private void setFAdapter(List<Video> response)
        {
        try {

            List<Video> videos = response;

            mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
            mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));


          runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    mFListView.setAdapter(mFAdapter);
                }
            });


            mProgressDialog.setVisibility(View.GONE);
            if (mFContainer.isRefreshing()) {
                mFContainer.setRefreshing(false);
            }
            if (mFAdapter.getCount() < mCount) {
                mFView.setVisibility(View.GONE);
                mFListView.removeFooterView(mFooterView);
            }
        }catch(Exception e){}
        }

    }

Solution 2:[2]

  1. Keep one singleton class object in hand. So that you can synchronize two thread on it. Care to be taken to not to block the ui thread.

  2. Reduce number of interfaces to only one method to start preparing data for your list and only one method to call your notifydatasetchanged/setAdapter on list.

Means there should be only one method like prepareData() which will be executed by a background thread. synchronise this method on your singleton object.

         MyListAdaper adapter = null;

         // Call this from a background thread
         public void prepareData() {
             synchronized (SingleTonProvider.getInstance()) {
                   List<AnyDataTypeYouWant> data = null;
                   // populate data here by your application logic.
                   adapter = new MyListAdaper(data);    
             }
         }   

And have only one method to refresh list.

         // Also Call this from a background thread only
         public void refreshList() {
               
             synchronized (SingleTonProvider.getInstance()) {
                   runOnUiThread(new Runnable() {
                       @Override
                       public void run() {
                           mFListView.setAdapter(adapter);
                       }
                   });
             }
         }
  1. have no other code on any place to prepare data and set data on list.
  2. Call the methods I mentioned from a background thread only.

I just gave general solution to your problem. You have to work on your specific case by yourself.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 halfer