0

Have such error, when trying to update listview in my fragment from asynctask:

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.

My AsyncTask class

protected Void doInBackground(Void... params) {
        int nextStep;
        BaseAnt tempAnt = null;
        ArrayList<BaseAnt> antList = new ArrayList<BaseAnt>();
        while (true) {
            Log.d("myLogs", "Start from APP");
            nextStep = RandomNumber.getRandomNumber(3);
            switch (nextStep) {
            case 0:
                tempAnt = new GuardAnt();
                AppStat.addToLog("Guard");
                break;
            case 1:
                tempAnt = new MotherAnt();
                AppStat.addToLog("Mother born");
                break;
            case 2:
                tempAnt = new WorkerAnt();
                AppStat.addToLog("Worker born");
                break;
            default:
                break;
            }
            antList.add(tempAnt);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            for (int i = 0; i < antList.size(); i++)
                antList.get(i).eat();
            publishProgress(String.valueOf(AppStat.WATER));
            if (AppStat.WATER < 0)
                break;
        }
        return null;

    }

    protected void onPostExecute(String result) {

    }

    @Override
    protected void onProgressUpdate(String... values) {
        Log.d("myLogs", "onProgressUpdate");
        LogFragment.mLogAdapter.notifyDataSetChanged();
        LogFragment.mLogAdapter.setSelectToLast();
        StatFragment.tvWater.setText(String.valueOf(AppStat.WATER));
        super.onProgressUpdate(values);
    }

AppStat is class extends Application, that cointain info about variables i need and method

    public static void addToLog(String str) {
    if (listLog.size() > 256)
        listLog.remove(0);
    listLog.add(getNowtime() + ": " + str);
}

In LogAdapter extends BaseAdapter:

    public void setSelectToLast() {
    Log.d("myLogs","UpdateToLast");
    notifyDataSetChanged();
    LogFragment.lvList.setSelection(getCount() + 1);
}

How to fix it?

Sunstrike
  • 456
  • 1
  • 6
  • 22

5 Answers5

2

You shouldn't call addToLog() (or any other UI operation) in doInBackground(). Either you collect everything in doInBackground, return it as a result and then update the list in onPostExecute, or you use publishProgress() and change the list content in onProgressUpdate()...

onPostExecute and onProgressUpdate are executed in the UI thread, doInBackground is executed in a different Thread.

jpm
  • 3,300
  • 1
  • 19
  • 29
  • What if my asynctask need to work a long time(i make a ant family simulator) OnPostExecute as i guess call only when asynctask finish work? – Sunstrike Nov 11 '13 at 14:30
  • Yeah, in that case you should use publishProgress. The data you pass in there will be available in onProgressUpdate – jpm Nov 11 '13 at 14:32
  • Look at the sample: http://developer.android.com/reference/android/os/AsyncTask.html – jpm Nov 11 '13 at 14:33
  • Ok, say if i all understand right: I need to coollect all my changes in log to temp array and then publish it all, not step by step? – Sunstrike Nov 11 '13 at 14:34
  • no, you can call publishProgress(data) whenever you want to update the UI (List) during doInBackground. Then you need to override onProgressUpdate(data). The data you passed to publishProgress() will then be available in onProgressUpdate() which is executed on the UI thread... 1) call publishProgress() in doInBackground instead of updating the UI there 2) update the UI in onProgressUpdate() – jpm Nov 11 '13 at 14:41
  • Check out the link. under 'usage' they have a simple example, where they use publishProgress and onProgressUpdate – jpm Nov 11 '13 at 14:42
  • Thx! It works. I just now make publishProgress with every logAdd, but add element to arraylist only in progressUpdate. One little question, i have LogFragment class extends Fragment and have listview static adapter in it. Is it bad to use static variable in such case? – Sunstrike Nov 11 '13 at 15:11
  • I'm not sure what you mean, but static listview adapter doesn't sound very good :) If the list is in a fragment and you want to update it from your activity, get a reference to your fragment and add a public method "updateList()" to it. Then call fragment.updateList() in onProgressUpdate. see this link for more details: http://developer.android.com/training/basics/fragments/communicating.html#Deliver – jpm Nov 11 '13 at 15:24
1

Ui updates are done from the onpostexecute() method within the asynctask. Do that and it will work fine as onpostexecute runs on the ui thread after doinbackground () has finished its job.

Niraj Adhikari
  • 1,678
  • 2
  • 14
  • 28
0

You should run the update in your main thread using something like this:

MainActivity.this.runOnUiThread(new runnable(){
public void run(){
    Log.d("UI thread", "I am the UI thread");
}

});

you can check this post also: Android basics: running code in the UI thread

Community
  • 1
  • 1
rui.mendes
  • 106
  • 5
0

Try using

MainActivity.this.runOnUiThread(new runnable()
{
  public void run()
  {
    LogFragment.mLogAdapter.notifyDataSetChanged();
    LogFragment.mLogAdapter.setSelectToLast();
    StatFragment.tvWater.setText(String.valueOf(AppStat.WATER));
  }
}

in your onProgressUpdate method

Apoorv
  • 13,470
  • 4
  • 27
  • 33
  • 1
    onProgressUpdate is executed in the mainthread. so there's no need to call runOnUiThread – jpm Nov 11 '13 at 14:28
0

Try calling notifyDataSetChanged() on your adapter in onPostExecute.

npace
  • 4,218
  • 1
  • 25
  • 35