Search code examples
androidandroid-asynctasklistactivity

java.lang.IllegalStateException: The content of the adapter has changed but ListView.... inspite of calling notifydatasetchanged()


What are the best practices to be followed to update the contents of a listactivty by a background thread (Async Task) ?

1) Am calling the notifyDataSetChanged() to update the adapter as soon as i manipulate the contents of the adapter but still my app force closes while the user scrolls or click on the list. Any pointers to prevent this would be very helpfull.

Logcat: java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification.

2) Where exaclty should i update contents of the listactivity ? inside the doInBackground() or onProgressUpdate()?

3) Am experiencing regular crashes when the user clicks the list item. So will disabling click events on the listactivty during the background operation solve the problem ? If so am not sure how to remove or set item click listeners dynamically to the listactivity. Please instruct me on the too.

4) I dont think blocking all ui interactions during the background async task execution is the only way to solve the problem. I know there is a simple way of doing this but need some help.

Thanks in advance.

This is my onCreate...

protected void onCreate(Bundle savedInstanceState) {    
super.onCreate(savedInstanceState);
setContentView(R.layout.fa);

tvStatus=(TextView) findViewById(R.id.tvStatus);

 adapter = new SimpleAdapter(
        this,
        mostPopularList,
        R.layout.list_item,
        new String[] {"title","author","views","date"},
        new int[] {R.id.textView1,R.id.textView2,R.id.textView4,R.id.textView3});
        //populateList();


        setListAdapter(adapter);
  }

My async task...

          private class LongOperation extends AsyncTask<String, Void, String> {

          @Override
          protected String doInBackground(String... params) {
        // code for adding new listactivty items 
          }      

          @Override
          protected void onPostExecute(String networkStatus) {
            adapter.notifyDataSetChanged();
          }

          @Override
          protected void onPreExecute() {

          }

          @Override
          protected void onProgressUpdate(Void... values) {

           }
          }

Logcat:

i rarely the the illegalstate exception too... not able to reproduce it. will post the logcat if i come across it again.

FATAL EXCEPTION: main
java.lang.IndexOutOfBoundsException: Invalid index 7, size is 4
    at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
    at java.util.ArrayList.get(ArrayList.java:311)
    at android.widget.SimpleAdapter.bindView(SimpleAdapter.java:147)
    at android.widget.SimpleAdapter.createViewFromResource(SimpleAdapter.java:126)
    at android.widget.SimpleAdapter.getView(SimpleAdapter.java:114)
    at android.widget.AbsListView.obtainView(AbsListView.java:1294)
    at android.widget.ListView.makeAndAddView(ListView.java:1727)
    at android.widget.ListView.fillDown(ListView.java:652)
    at android.widget.ListView.fillGap(ListView.java:623)
    at android.widget.AbsListView.trackMotionScroll(AbsListView.java:2944)
    at android.widget.AbsListView.onTouchEvent(AbsListView.java:2065)
    at android.widget.ListView.onTouchEvent(ListView.java:3315)
    at android.view.View.dispatchTouchEvent(View.java:3766)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:897)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1671)
    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
    at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1655)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1671)
    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
    at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1655)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:4627)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:521)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)

Solution

  • 2) Where exaclty should i update contents of the listactivity ? inside the doInBackground() or onProgressUpdate()?

    Inside onProgressUpdate() or inside onPostExecute() because it could take some time since you change content in doInBackground() and call async task calls your

              @Override
              protected void onPostExecute(String networkStatus) {
                adapter.notifyDataSetChanged();
              }
    

    3) Am experiencing regular crashes when the user clicks the list item. So will disabling click events on the listactivty during the background operation solve the problem ? If so am not sure how to remove or set item click listeners dynamically to the listactivity. Please instruct me on the too.

    It is not a solution. You just need update your dataset on UI thread. (Not load content, just update from old to a new one). For example

    private class LongOperation extends AsyncTask<String, Void, String> {
    
          @Override
          protected List<String> doInBackground(String... params) {
               List<String> items = loadUpdatedDataset(params);
               return items;
          }      
    
          @Override
          protected void onPostExecute(List<String> loadedItems) {
            updateAdapterDataset(loadedItems);
            adapter.notifyDataSetChanged();
          }
    
          @Override
          protected void onPreExecute() {
    
          }
    
          @Override
          protected void onProgressUpdate(Void... values) {
    
           }
          }