5

I've been struggling with this for some time now. In my project there are several Activities that include a ListView and a custom adapter extending BaseAdapter. They also implement some interfaces which usually inform it that the data has been changed.

However, when I call the notifyDataSetChanged() from the listener/interface method implemented in my class, the ListView is not updated and none of the adapter methods are called (getCount(), getView()).

I know this has something to do with the fact where the notifyDataSetChanged() is called from, I mean which thread, but I don't quite understand it and couldn't find a straightforward explanation of this behavior.

As a workaround of this problem I use a Handler which calls a method periodically and in that method I look if a boolean 'needsToBeUpdated' value is true and I call the notifyDataSetChanged() method then. This is ugly, though, and I am sure there needs to be a way to do it asynchronously.

Any help would be appreciated.

Pseudo-code of what I'm talking about:

public class FriendsActivity extends Activity implements FriendsListener {
private ListView mListView;
private ArrayList<Friends> mFriendsList;
private FriendsAdapter mFriendsAdapter;
private boolean mNeedsToBeUpdated;
private Handler mListUpdateHandler;
private Runnable mListUpdateTask;

onCreate() {
    initViews();
    mFriendsAdapter = new FriendsAdapter(mFriendsList);
    mListView.setAdapter(mFriendsAdapter);
    SomeStaticClass.addFriendListener(this)

    mNeedsToBeUpdated = false;

    mListUpdateHandler = new Handler();
    mListUpdateHandler.removeCallbacks(mListUpdateTask);
    mListUpdateHandler.postDelayed(mListUpdateTask, 10000);

}

onListenerMethod() {
    updateFriendsList();
    mFriendsAdapter.updateDataSource(mFriendsList);
    mFriendsAdapter.notifyDataSetChanged(); // THIS DOESN'T UPDATE THE VIEWS
    mNeedsToBeUpdated = true;
}

protected void onResume() {
    mListUpdateTask = new Runnable() {
        public void run() {
            if (mNeedsToBeUpdated ) {
                updateFriendsList();
                mFriendsAdapter.updateDataSource(mFriendsList);
                mFriendsAdapter.notifyDataSetChanged(); // THIS UPDATES THE VIEWS
                mListsRequireUpdating = false;
            }
            mListUpdateHandler.postDelayed(mListUpdateTask, 10000);
        }
    };
    mListUpdateHandler = new Handler();
    mListUpdateHandler.removeCallbacks(mListUpdateTask);
    mListUpdateHandler.post(mListUpdateTask);
    super.onResume();
}

EDIT: Of course it took me almost no time to find the answer once I posted this here...

I hate to do it but I put some more effort into looking for an answer and I think I found it thanks to this: http://developer.android.com/resources/articles/painless-threading.html - and this: http://developer.android.com/reference/android/os/Handler.html

The solution is painfully simple. It is not possible to change the UI in any other thread but the main/UI thread. That's why doing it in the callback methods doesn't work. However, creating a Handler in the UI Thread (e.g. in the onCreate() method) connects it to the UI Thread and can be later used to post events to this thread.

mListUpdateHandler = new Handler();

Then, an inner class implementing the Runnable interface is needed where the UI tweaking is done.

class UpdateListRunnable implements Runnable {
        @Override
        public void run() {
            Log.i(LOGTAG, "UpdateListRunnable");
            FriendsActivity.this.updateLists();            
        }
    }

Finally, in the callback method we post the event with out UpdateListRunnable class to the main thread via the Handler:

@Override
    public void entriesUpdated(Collection<String> entries) {
        Log.i(LOGTAG, "entriesUpdated");
        for (String entry : entries) {
            Log.i(LOGTAG, "entry: " + entry);
        }
        mListUpdateHandler.post(new UpdateListRunnable());
    }

Thanks to that the upadteLists() method is run in the UI thread and everything works like a charm.

Bogdan Mitrache
  • 10,536
  • 19
  • 34
PawelPredki
  • 754
  • 1
  • 12
  • 26

1 Answers1

0

I had the same problem with my application. The first solution was to use Handlers. The more elegant one is to use Loaders - check http://developer.android.com/guide/components/loaders.html! The compatibility package for older android versions make them available for Android 1.6+

maddob
  • 989
  • 1
  • 12
  • 29