Java – ContentResolver.notifyChange does not work

ContentResolver.notifyChange does not work… here is a solution to the problem.

ContentResolver.notifyChange does not work

I’d like to play with a ContentProvider example, but I’m running into a problem that I can’t seem to fix.

This example contains an Activity:

import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity
{

final String LOG_TAG = "myLogs";

final Uri CONTACT_URI = Uri.parse("content://zulfigarov.com.trainingprj.MyContactsProvider/contacts");

final String CONTACT_NAME = "name";
    final String CONTACT_EMAIL = "email";

@Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

Cursor cursor = getContentResolver().query(CONTACT_URI, null, null, null, null);

startManagingCursor(cursor);

String[] from = {"name", "email"};
        int[] to = {android. R.id.text1, android. R.id.text2};

SimpleCursorAdapter adapter
                = new SimpleCursorAdapter(this, android. R.layout.simple_list_item_2, cursor, from, to, 0);

ListView lvContact = (ListView)findViewById(R.id.lvContacts);
        lvContact.setAdapter(adapter);
    }

public void onClickInsert(View view)
    {
        ContentValues cv = new ContentValues();
        cv.put(CONTACT_NAME, "name 4");
        cv.put(CONTACT_EMAIL, "email 4");
        Uri newUri = getContentResolver().insert(CONTACT_URI, cv);

Log.d(LOG_TAG, "insert, result Uri: " + newUri.toString());
    }

public void onClickUpdate(View view)
    {
        ContentValues cv = new ContentValues();
        cv.put(CONTACT_NAME, "name 5");
        cv.put(CONTACT_EMAIL, "email 5");
        Uri uri = ContentUris.withAppendedId(CONTACT_URI, 2);
        int cnt = getContentResolver().update(uri, cv, null, null);

Log.d(LOG_TAG, "update, count = " + cnt);
    }

public void onClickDelete(View view)
    {
        Uri uri = ContentUris.withAppendedId(CONTACT_URI, 3);
        int cnt = getContentResolver().delete(uri, null, null);

Log.d(LOG_TAG, "delete, count = " + cnt);
    }

public void onClickError(View view)
    {
        Uri uri = Uri.parse("content://zulfigarov.com.trainingprj.MyContentProvider/phones");

try
        {
            Cursor cursor = getContentResolver().query(uri, null, null, null, null);

}
        catch (Exception ex)
        {
            Log.d(LOG_TAG, "Error: " + ex.getClass() + ", " + ex.getMessage());
        }

}
}

and a ContentProvider:

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class MyContactsProvider extends ContentProvider
{

final String LOG_TAG = "myLogs";

static final String DB_NAME = "mydb";
    static final int DB_VERSION = 1;

static final String CONTACT_TABLE = "contacts";

static final String CONTACT_ID = "_id";
    static final String CONTACT_NAME = "name";
    static final String CONTACT_EMAIL = "email";

static final String DB_CREATE = "CREATE TABLE " + CONTACT_TABLE + "("
                                     + CONTACT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                                     + CONTACT_NAME + " TEXT, "
                                     + CONTACT_EMAIL + " TEXT" + ");";

static final String AUTHORITY = "zulfigarov.com.trainingprj.MyContactsProvider";

static final String CONTACT_PATH = "contacts";

public static final Uri CONTACT_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + CONTACT_PATH);

static final String CONTACT_CONTENT_TYPE = "vnd.android.cursor.dir/vnd." + AUTHORITY + "." + CONTACT_PATH;

static final String CONTACT_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd." + AUTHORITY + "." + CONTACT_PATH;

static final int URI_CONTACTS = 1;

static final int URI_CONTACTS_ID = 2;

private static final UriMatcher uriMatcher;
    static
    {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, CONTACT_PATH, URI_CONTACTS);
        uriMatcher.addURI(AUTHORITY, CONTACT_PATH + "/#", URI_CONTACTS_ID);
    }

DBHelper dbHelper;
    SQLiteDatabase db;

@Override
    public boolean onCreate()
    {
        Log.d(LOG_TAG, "onCreate provider");
        dbHelper = new DBHelper(getContext());
        return true;
    }

@Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    {
        Log.d(LOG_TAG,"query, " + uri.toString());

switch (uriMatcher.match(uri))
        {
            case URI_CONTACTS:
                Log.d(LOG_TAG, "URI_CONTACTS");
                if(TextUtils.isEmpty(sortOrder))
                {
                    sortOrder = CONTACT_NAME + " ASC";
                }
                break;

case URI_CONTACTS_ID:
                String id = uri.getLastPathSegment();
                Log.d(LOG_TAG, "URI_CONTACTS_ID");

if (TextUtils.isEmpty(selection))
                    selection = CONTACT_ID + " = " + id;
                else
                    selection = selection + " AND " + CONTACT_ID + " = " + id;
                break;

default:
                throw new IllegalArgumentException("Wrong URI: " + uri);
        }

db = dbHelper.getWritableDatabase();
        Cursor cursor = db.query(CONTACT_TABLE, projection, selection,
                selectionArgs, null, null, sortOrder);

cursor.setNotificationUri(getContext().getContentResolver(),CONTACT_CONTENT_URI);

return cursor;
    }

@Override
    public String getType(Uri uri)
    {
        Log.d(LOG_TAG, "getTYpe, " + uri.toString());
        switch (uriMatcher.match(uri))
        {
            case URI_CONTACTS:
                return CONTACT_CONTENT_TYPE;
            case URI_CONTACTS_ID:
                return CONTACT_CONTENT_ITEM_TYPE;
        }
        return null;
    }

@Override
    public Uri insert(Uri uri, ContentValues values)
    {
        Log.d(LOG_TAG,"insert, " + uri.toString());

if(uriMatcher.match(uri) != URI_CONTACTS)
            throw new IllegalArgumentException("Wrong URI: " + uri);

db = dbHelper.getWritableDatabase();
        long rowID = db.insert(CONTACT_TABLE, null, values);
        Uri resultUri = ContentUris.withAppendedId(CONTACT_CONTENT_URI, rowID);
        getContext().getContentResolver().notifyChange(resultUri, null);

return resultUri;
    }

@Override
    public int delete(Uri uri, String selection, String[] selectionArgs)
    {

Log.d(LOG_TAG,"delete, " + uri.toString());
        switch (uriMatcher.match(uri))
        {
            case URI_CONTACTS:
                Log.d(LOG_TAG,"URI_CONTACTS");
                break;

case URI_CONTACTS_ID:
                String id = uri.getLastPathSegment();
                Log.d(LOG_TAG,"URI_CONTACTS_ID, " + id);
                if(TextUtils.isEmpty(selection))
                {
                    selection = CONTACT_ID + " = " + id;
                }
                else
                {
                    selection = selection + " AND " + CONTACT_ID + " = " + id;
                }
                break;

default:
                throw new IllegalArgumentException("Wrong URI: " + uri);
        }

db = dbHelper.getWritableDatabase();
        int cnt = db.delete(CONTACT_TABLE, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);

return cnt;
    }

@Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    {
        Log.d(LOG_TAG,"update, " + uri.toString());

switch (uriMatcher.match(uri))
        {
            case URI_CONTACTS:
                Log.d(LOG_TAG,"URI_CONTACTS");
                break;
            case URI_CONTACTS_ID:
                String id = uri.getLastPathSegment();
                Log.d(LOG_TAG, "URI_CONTACTS_ID");
                if(TextUtils.isEmpty(selection))
                {
                    selection = CONTACT_ID;
                }
                else
                {
                    selection = selection + " AND " + CONTACT_ID + " = " + id;
                }
                break;
            default:
                throw new IllegalArgumentException("Wrong URI: " + uri);
        }
        db = dbHelper.getWritableDatabase();
        int cnt = db.update(CONTACT_TABLE, values, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri,null);
        return cnt;
    }

private class DBHelper extends SQLiteOpenHelper
    {

public DBHelper(Context context)
        {
            super(context, DB_NAME, null, DB_VERSION);
        }

@Override
        public void onCreate(SQLiteDatabase db)
        {
            db.execSQL(DB_CREATE);
            ContentValues cv = new ContentValues();

for (int i = 1; i <= 3; i++)
            {
                cv.put(CONTACT_NAME, "name " + i);
                cv.put(CONTACT_EMAIL, "email " + i);
                db.insert(CONTACT_TABLE, null, cv);
            }
        }

@Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
        {

}
    }
}

The trouble is that when I click the button (triggering methods in the MainActivity such as onClickInsert, onClickUpdate, etc.), it updates the database, but not the activity’s ListView. Looks like

getContext().getContentResolver().notifyChange(resultUri, null);

Not working. So I couldn’t find out where I was wrong.

Solution

Use CursorLoaders to load data and populate the ListView.

http://developer.android.com/reference/android/content/CursorLoader.html

Then use getContext().getContentResolver().notifyChange(resultUri, null) when inserting, updating, or deleting it; !

Without CursorLoaders, you would have to use ContentObservers

So even though the DB is updating in the background and you’re using notifyChange() no one is listening to it!

Related Problems and Solutions