0

I have a ListView which I need to populate using a background thread. The list needs to update as each item is retrieved. Below is a very simplified example of how I implement this.

  public class DownloadTask extends AsyncTask <MyUserObject, Integer, String>
  {
    @Override
    protected MyUserObject doInBackground(MyUserObject... myUserObj) 
    {
        MyUserObject muo = null;
        int nCount = myUserObj.length;
        if( nCount > 0 )
            muo = myUserObj[0];

        muo.DownloadStuff();
        return muo.getUserName();
    }

    protected void onPostExecute(String userName)
    {
        adapter.names.add(userName);
        adapter.notifyDataSetChanged();
    }
}

public class MyAdapterClass extends BaseAdapter 
{
    private ArrayList<String>names;

    public MyAdapterClass(Context context)
    {
        names = new ArrayList<String>();
    }

    public fillList()
    {
         for( int i=0; i<users.length; i++ )
         {
             DownloadTask task = new DownloadTask();
             task.execute(users[i]);
         }
    }

In the above example, 'adapter' is an object of type MyAdapterClass, and its fillList() method is what launches the threads. Calling notifyDataSetChanged() in onPostExecute() is what updates my ListView as data arrives.

The problem is, that I am accessing my sqlite database in "DownloadStuff()' which is called in 'doInBackground', and having multiple threads accessing the DB causes it to crash. (If I comment out all DB activities in here, then it runs fine). Below is how I try to workaround this problem, however it still crashes. Any advice on how I can have my ListView update as data is retrieved from a background thread?

 Semaphore semaphore = new Semaphore(1, true);

 public synchronized void DownloadStuff()
 {
     semaphore.acquire(1);
     // ... DB operations ... //
     semaphore.release(1);
 }
lost_bits1110
  • 157
  • 1
  • 5
  • 18

2 Answers2

1

I think your approach is wrong from it's beginning. Why do you want to start separate AsyncTask for each item you have to add to your adapter. Use onProgressUpdate to notify the gui for newly added items in the adapter. In this case you want have concurrent access to your db.

Mojo Risin
  • 8,136
  • 5
  • 45
  • 58
  • its true that if you can centralize you async work into one its better (and would avoid the synchronized issues ^^ – Jason Rogers Jun 23 '11 at 22:40
  • Thank-you, I didn't like the multiple threads either and missed onProgressUpdate, this works! – lost_bits1110 Jun 24 '11 at 00:21
  • My new concern is that AsyncTask never gets destroyed, even if the Activity is destroyed. Quite strange, something tells me I should not have used AsyncTask :s – lost_bits1110 Jun 24 '11 at 00:31
  • I am not sure about that. AsyncTask are very helpful in case when you have to do something in background and return the result in the UI thread. What are your concerns about the AsyncTask. – Mojo Risin Jun 24 '11 at 05:32
  • Well, I just noticed that the AsyncTask keeps running (I can see it in Debug view), even when my Activity has been destroyed, and this was worrying me. I have seen other posts about this, and I now add checks to "IsCancelled()" in "doInBackground" as recommended by some of the posts. I guess I'm not sure what else I need to account for now that I know that AsyncTasks never die. – lost_bits1110 Jun 24 '11 at 18:04
  • @lost_bits1110: [AsyncTasks don't die because Android is pooling threads](http://stackoverflow.com/questions/3077461/asynctask-threads-never-die-android) (and that's a good thing). – idbrii Jul 14 '11 at 21:29
0

I'm not sure (because I'm really tired) but I think your ot using you synchronysed correctly.

you create a different instance of MyUserObject each time you do a async task, this means you never actually call Downloadstuff on the same instance hence no conflict, but on the other hand your database is unique being called by multiple MyUserObject hence conflict.

what you want to do is have the same instance of muo in all your async task, this way they all call downloadstuff on the same instance and then synchronized will work preventing multiple access.

you also don't need the semaphoe here.


edit:

Mojo Risin answer is also very good, if you can save yourself the trouble by centralizing all you async tasks into one you should(less concurrent threads running around you have the better)

Jason Rogers
  • 19,194
  • 27
  • 79
  • 112
  • I didn't get a chance to try out your solution, as I decided to try and avoid having concurrent threads, thank-you though for your comments! – lost_bits1110 Jun 24 '11 at 00:21