Search code examples
androidnotificationsandroid-contentprovidersimplecursoradapter

ContentResolver.notifyChange() doesn't work


Some background information:

When I run my application, a foreground activity displays cached information from my local SQLite database and meanwhile a background service connects to server and gets some fresh information. When data is retrieved, the service fires up my ContentProvider's insert method, to update my local SQLite database. When the insert is done, I notify my ContentObserver (foreground activity) about the change to refresh the view.

I know the insert works fine, because when I rerun the application manually I can see new data on my screen. Unfortunately, auto refresh does not work. Calling the method ContentObserver.notifyChange() fails. The weirdest thing is that it doesn't even stop on the catch (Exception e) line. It goes straight outside the try/catch block. I have no idea what's going on. LogCat, and console are quiet.

Here's the part of code from insert method within my ContentProvider responsible for insert and change notification.

SQLiteDatabase sqlDB = db.getWritableDatabase();
try {
    long newID = sqlDB.insertOrThrow(StatsCollectorDatabase.TABLE_USERS, null, values);
    if (newID > 0) {
        Uri newUri = ContentUris.withAppendedId(uri, newID);
        ContentResolver contentResolver = getContext().getContentResolver();
        contentResolver.notifyChange(uri, null); // This is the line that fails
        return newUri;
    } else {
        throw new SQLException("Failed to insert row into " + uri);
    }
} catch (SQLiteConstraintException e) {
    Log.i(DEBUG_TAG, "Ignoring constraint failure.");
} catch (Exception e) {
    Log.i(DEBUG_TAG, e.getLocalizedMessage());
}

Here's the code from onCreate method in foreground Activity:

Cursor cursor = this.managedQuery(StatsCollectorProvider.CONTENT_URI, null, null, null, null);
String[] columns = new String[] { StatsCollectorDatabase._USER_NAME, StatsCollectorDatabase._USER_DEVICE_ID };
int[] views = new int[] { R.id.name_entry, R.id.number_entry };

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this.getApplicationContext(), R.layout.list_item, cursor, columns, views, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
this.setListAdapter(adapter);

Intent intent = new Intent(this.getApplicationContext(), UsersDownloaderService.class);
this.startService(intent);    

I tried googling, but found nothing relevant. How do I fix this problem?

I can provide more code if this'll be necessary.


Solution

  • I solved it. My activity didn't inherit FragmentActivity and my layout didn't contain any fragments, so it could not work - there wasn't any registered ContentObservers.

    Now it looks like this:

    First the main activity takes off, registering the fragment layout and starting the background service in it's onCreate method:

    super.onCreate(savedInstanceState);
    setContentView(R.layout.list);
    
    Intent intent = new Intent(this.getApplicationContext(), UsersDownloaderService.class);
    this.startService(intent);  
    

    This is my list layout:

    <?xml version="1.0" encoding="utf-8"?>
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        class="org.moyshe.cursors.ListViewFragment"
        android:name="org.moyshe.cursors.ListViewFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </fragment>
    

    Class ListViewFragment extends ListFragment and implements LoaderCallbacks<Cursor>. This is my ContentObserver i've been missing. If anyone's interested on how it looks, please read this article - it helped me a lot.

    My Service and ContentProvider classes didn't change.