16

I have a database, a ListView, and a CustomCursorAdapter that extends CursorAdapter. A menu button adds an item to the database. I want the ListView to update and show this change. Normally it doesn't show this new item until i go to the homescreen and reopen the application.

I did eventually get it to work by calling cursor.requery() or mCustomCursorAdapter.changeCursor(newCursor) whenever I added a new item, but when I set autoRequery to false in the CursorAdapter constructor, it worked just the same. Why does it update correctly when autoRequery is set to false?

Am I using CursorAdapter correctly? What is the standard way of keeping the list updated with the database? And what does autoRequery do?

user
  • 86,916
  • 18
  • 197
  • 190
user316146
  • 1,307
  • 1
  • 12
  • 19

2 Answers2

38

The idiomatic and imho correct way to automatically update Cursors is to call Cursor#setNotificationUri when they are created and before they are handed off to whatever requested them. Then call ContentResolver#notifyChange when anything in that Cursor's Uri's namespace changes.

For example, suppose you were creating a simple mail application and you wanted to update when new mail arrived but also provide various views on the mail. I'd have some basic Uri's defined.

content://org.example/all_mail
content://org.example/labels
content://org.example/messages

Now, say I wanted to get a cursor that gave me all mail and be updated when new mail arrives:

Cursor c;
//code to get data
c.setNotificationUri(getContentResolver(), Uri.parse("content://org.example/all_mail");

Now new mail arrives so I notify:

//Do stuff to store in database
getContentResolver().notifyChange(Uri.parse("content://org.example/all_mail", null);

I should also notify all the Cursors that selected for labels this new message met

for(String label : message.getLabels() {
  getContentResolver().notifyChange(Uri.parse("content://org.example/lables/" + label, null);
}

And also, maybe a cursor is viewing that one specific message so notify them as well:

getContentResolver().notifyChange(Uri.parse("content://org.example/messages/" + message.getMessageId(), null);

The getContentResolver() calls happen where the data is accessed. So if it's in a Service or ContentProvider that is where you setNotificationUri and notifyChange. You should not be doing that from where the data is accessed, e.g., an Activity.

AlarmProvider is a simple ContentProvider that uses this method to update Cursors.

Rich Schuler
  • 41,814
  • 6
  • 72
  • 59
  • This post is really great! Helped me a lot as well on my search! :) – Patrick Boos Dec 16 '10 at 07:20
  • Correct me if I am wrong `Cursor#setNotificationUri` updates the cursor with the latest data whenever their is a `getContentResolver().notifyChanged` is a called in `ContentProvider`. – Gaurav Agarwal May 21 '12 at 09:15
  • Could you please clarify, will automatically update data when you notify change or you need to do anything else in Cursor/CursorAdapter? – RiaD Jul 12 '13 at 08:00
1

I created next method for ListView updating:

/**
 * Method of refreshing Cursor, Adapter and ListView after database 
 * changing
 */
public void refreshListView() {
    databaseCursor = db.getReadableDatabase().query(
            CurrentTableName, 
            null, 
            null, 
            null, 
            null, 
            null, 
            "title"+SortingOrder);
    databaseListAdapter = new DomainAdapter(this, 
            android.R.layout.simple_list_item_2, 
            databaseCursor, 
            new String[] {"title", "description"}, 
            new int[] { android.R.id.text1, android.R.id.text2 });
    databaseListAdapter.notifyDataSetChanged();
    DomainView.setAdapter(databaseListAdapter);
}

end calls it each time after some changing in database

Anton Derevyanko
  • 3,405
  • 1
  • 24
  • 32