Android, ListView IllegalStateException: “The content of the adapter has changed but ListView did not receive a notification”

后端 未结 25 2115
执念已碎
执念已碎 2020-11-22 16:59

What I want to do: run a background thread which calculates ListView contents and update ListView partially, while results are calculated.

W

相关标签:
25条回答
  • 2020-11-22 17:48

    I had the same problem, but I fixed it using the method

    requestLayout();
    

    from the class ListView

    0 讨论(0)
  • 2020-11-22 17:48

    My solution:

    1) create a temp ArrayList.

    2) do your heavy works (sqlite row fetch , ...) in doInBackground method and add items to the temp arraylist.

    3) add all items from temp araylist to your listview's arraylist in onPostExecute method.

    note: you may want to delete some items from listview and also delete from sqlite database and maybe delete some files related to items from sdcard , just remove items from database and remove their related files and add them to temp arraylist in background thread. then in UI thread delete items existing in temp arraylist from the listview's arraylist.

    Hope this helps.

    0 讨论(0)
  • 2020-11-22 17:50

    Like @Mullins said "
    I both added the items and called notifyDataSetChanged() in the UI thread and I resolved this. – Mullins".

    In my case I have asynctask and I called notifyDataSetChanged() in the doInBackground() method and the problem is solved, when I called from onPostExecute() I received the exception.

    0 讨论(0)
  • 2020-11-22 17:51

    This is a MultiThreading Issue and Using Properly Synchronized Blocks This can be prevented. Without putting extra things on UI Thread and causing loss of responsiveness of app.

    I also faced the same. And as the most accepted answer suggests making change to adapter data from UI Thread can solve the issue. That will work but is a quick and easy solution but not the best one.

    As you can see for a normal case. Updating data adapter from background thread and calling notifyDataSetChanged in UI thread works.

    This illegalStateException arises when a ui thread is updating the view and another background thread changes the data again. That moment causes this issue.

    So if you will synchronize all the code which is changing the adapter data and making notifydatasetchange call. This issue should be gone. As gone for me and i am still updating the data from background thread.

    Here is my case specific code for others to refer.

    My loader on the main screen loads the phone book contacts into my data sources in the background.

        @Override
        public Void loadInBackground() {
            Log.v(TAG, "Init loadings contacts");
            synchronized (SingleTonProvider.getInstance()) {
                PhoneBookManager.preparePhoneBookContacts(getContext());
            }
        }
    

    This PhoneBookManager.getPhoneBookContacts reads contact from phonebook and fills them in the hashmaps. Which is directly usable for List Adapters to draw list.

    There is a button on my screen. That opens a activity where these phone numbers are listed. If i directly setAdapter over the list before the previous thread finishes its work which is fast naviagtion case happens less often. It pops up the exception .Which is title of this SO question. So i have to do something like this in the second activity.

    My loader in the second activity waits for first thread to complete. Till it shows a progress bar. Check the loadInBackground of both the loaders.

    Then it creates the adapter and deliver it to the activity where on ui thread i call setAdapter.

    That solved my issue.

    This code is a snippet only. You need to change it to compile well for you.

    @Override
    public Loader<PhoneBookContactAdapter> onCreateLoader(int arg0, Bundle arg1) {
        return new PhoneBookContactLoader(this);
    }
    
    @Override
    public void onLoadFinished(Loader<PhoneBookContactAdapter> arg0, PhoneBookContactAdapter arg1) {
        contactList.setAdapter(adapter = arg1);
    }
    
    /*
     * AsyncLoader to load phonebook and notify the list once done.
     */
    private static class PhoneBookContactLoader extends AsyncTaskLoader<PhoneBookContactAdapter> {
    
        private PhoneBookContactAdapter adapter;
    
        public PhoneBookContactLoader(Context context) {
            super(context);
        }
    
        @Override
        public PhoneBookContactAdapter loadInBackground() {
            synchronized (SingleTonProvider.getInstance()) {
                return adapter = new PhoneBookContactAdapter(getContext());    
            }
        }
    
    }
    

    Hope this helps

    0 讨论(0)
  • 2020-11-22 17:53

    This is a known bug in Android 4 to 4.4(KitKat) and is resolved in ">4.4"

    See here: https://code.google.com/p/android/issues/detail?id=71936

    0 讨论(0)
  • 2020-11-22 17:53

    I faced a similar problem, here's how I solved in my case. I verify if the task already is RUNNING or FINISHED because an task can run only once. Below you will see a partial and adapted code from my solution.

    public class MyActivity... {
        private MyTask task;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
           // your code
           task = new MyTask();
           setList();
        }
    
        private void setList() {
        if (task != null)
            if (task.getStatus().equals(AsyncTask.Status.RUNNING)){
                task.cancel(true);
                task = new MyTask();
                task.execute();         
            } else if (task.getStatus().equals(AsyncTask.Status.FINISHED)) {
                task = new MyTask();
                task.execute();
            } else 
                task.execute();
        }
    
        class MyTask extends AsyncTask<Void, Item, Void>{
           List<Item> Itens;
    
           @Override
           protected void onPreExecute() {
    
            //your code
    
            list.setVisibility(View.GONE);
            adapterItem= new MyListAdapter(MyActivity.this, R.layout.item, new ArrayList<Item>());
            list.setAdapter(adapterItem);
    
            adapterItem.notifyDataSetChanged();
        }
    
        @Override
        protected Void doInBackground(Void... params) {
    
            Itens = getItens();
            for (Item item : Itens) {
                publishProgress(item );
            }
    
            return null;
        }
    
        @Override
        protected void onProgressUpdate(Item ... item ) {           
            adapterItem.add(item[0]);
        }
    
        @Override
        protected void onPostExecute(Void result) {
            //your code
            adapterItem.notifyDataSetChanged();     
            list.setVisibility(View.VISIBLE);
        }
    
    }
    
    }
    
    0 讨论(0)
提交回复
热议问题