Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContentLoader not properly refreshing after database changes

I have an SQLiteDatabase whose data is managed by a Content Provider. I'm using a tabbed layout. The first tab has the ability to add rows to the database, whereas the second tab shows items from the database. As I add items to the database from the first tab, the changes should be reflected when I move to the other tab.

Data is being added to the database correctly, and upon first opening of the app, all the current data (and anything new added in a previous version of the app) will appear. However, adding new items to the database is not reflected in the ListFragment.

@Override
    public void onClick(View v) {
        if(v == addSale) {
            Item item = new Item(rn(), null, 100);
            data.add(item);
            total += item.getAmount();
        } else if(v == save) {
            for(Item i: data) {
                ContentValues cv = new ContentValues();
                cv.put(DatabaseProvider.COLUMN_COST, i.getAmount());
                cv.put(DatabaseProvider.COLUMN_ITEM, i.getItem());
                cv.put(DatabaseProvider.COLUMN_PERSON, i.getPerson());
                this.getActivity().getContentResolver().insert(DatabaseProvider.CONTENT_URI, cv);
            }
            total = 0;
            data.clear();
        } else if(v == clear) {
            data.clear();
            total = 0;
        }
        items.notifyDataSetChanged();
        totalView.setText(Item.format(total));
    }

Here is where I add the items to the database specifically with these lines:

ContentValues cv = new ContentValues();
cv.put(DatabaseProvider.COLUMN_COST, i.getAmount());
cv.put(DatabaseProvider.COLUMN_ITEM, i.getItem());
cv.put(DatabaseProvider.COLUMN_PERSON, i.getPerson());
this.getActivity().getContentResolver().insert(DatabaseProvider.CONTENT_URI, cv);

As I said earlier, items are put into the database correctly, so I'm reasonably sure that this is correct.

Here is the insert method of my DatabaseProvider

@Override
public Uri insert(Uri uri, ContentValues initialValues) {
    if (sUriMatcher.match(uri) != TABLE) {
        throw new IllegalArgumentException("Invalid URI: " + uri);
    }

    if (initialValues == null) {
        initialValues = new ContentValues();
    }

    SQLiteDatabase db = dbHelper.getWritableDatabase();

    long rowId = db.insert(TABLE_SALES, COLUMN_COST, initialValues);

    if (rowId > 0) {
        Uri newUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
        getContext().getContentResolver().notifyChange(uri, null);
        return newUri;
    }

    throw new SQLException("Failed to insert row into: " + uri);
}

From the various tutorials and other SO questions, it seems as if

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

is the key to getting it to update, and it's there, and is called. But nothing updates.

Finally, my list fragment that display all of the data.

package org.worldsproject.android.barcode;

import org.worldsproject.android.barcode.database.DatabaseProvider;

import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;

import com.actionbarsherlock.app.SherlockListFragment;

public class RunningTotal extends SherlockListFragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    public static final String TAG = "Running Total";

    public static final int RUNNING_TOTAL_ID = 1;
    private SimpleCursorAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] uiBindFrom = { DatabaseProvider.COLUMN_PERSON };
        int[] uiBindTo = { R.id.titled };

        adapter = new SimpleCursorAdapter(
                getActivity().getApplicationContext(), R.layout.list_title,
                null, uiBindFrom, uiBindTo,
                CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

        setListAdapter(adapter);
        getLoaderManager().initLoader(RUNNING_TOTAL_ID, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
        String[] projection = { DatabaseProvider.COLUMN_ID,
                DatabaseProvider.COLUMN_PERSON};
        return new CursorLoader(getActivity(), DatabaseProvider.CONTENT_URI,
                projection, null, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
        // TODO Auto-generated method stub
        adapter.swapCursor(cursor);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        // TODO Auto-generated method stub
        adapter.swapCursor(null);
    }

}

It's nearly a direct clone of http://mobile.tutsplus.com/tutorials/android/android-sdk_loading-data_cursorloader/ and at the moment just displays the name column of each row in the database. It shows previous entries, but as I've said, doesn't update. What step am I missing to get it to update?

like image 209
Atrus Avatar asked Oct 19 '12 00:10

Atrus


2 Answers

I think your

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

may be fine.

I can't see your query function for your DatabaseProvider, but did you remember to set the notificationUri for the cursor you are returning in your query function?

In your query() function of the DatabaseProvider, you should set the notification Uri for the cursor, or else the cursor won't get a notification even if you do a notifyChange():

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
   // blah blah
   cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
like image 153
hiBrianLee Avatar answered Nov 07 '22 16:11

hiBrianLee


I had a similar problem with a Fragment using a view pager. I realized that I didn't have a ContentObserver registered, so all I did was add this code to the onResume()

getActivity().getContentResolver().registerContentObserver(sUri, true, myObserver);

and have myObserver declared as a member variable:

new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        getActivity().getSupportLoaderManager().restartLoader(); // With appropriate loader args here
    }
});

And make sure to unregister is in the onPause();

That seemed to fix my problem, assuming that the ContentProvider is calling notifyChange correctly (It looks like you are, though).

Hope this helps you out!

like image 39
frenziedherring Avatar answered Nov 07 '22 17:11

frenziedherring