mmlooloo
mmlooloo

Reputation: 18977

android getContentResolver().notifyChange() does not restart my loader

codes:

First my Uris

public static final String PACKAGE    = "my.url.contentprovider";

public static final String TABLE_NAME = "NetworkTransaction";

public static final String AUTHORITY  = PACKAGE + ".NetTransContentProvider";

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

public static final Uri CONTENT_URI_ANY_OBSERVER  = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/*");

public static final Uri CONTENT_URI_FIND_BY_ID  = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/FIND/ID");

public static final Uri CONTENT_URI_INSERT_OR_REPLACE_BY_ID    = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/INSERT/REPLACE/ID"); 

public static final Uri CONTENT_URI_INSERT_BY_ID   = Uri.withAppendedPath(BASE_URI,TABLE_NAME+"/INSERT/ID"); 

and my loader from activity code:

@Override
protected void onResume() {
    super.onResume();
getSupportLoaderManager().restartLoader(NET_TRANS_LOADER_ID,mBundle,this).forceLoad();

}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
    Uri uri = null;
    CursorLoader cl=null;
    switch (id) {
    case NET_TRANS_LOADER_ID:
        uri = NetTransContentProvider.CONTENT_URI_FIND_BY_ID;
        cl  = new CursorLoader(ChoosingUserNameActivity.this, uri, NetTransDbUtils.allColumns,
                NetTransDbUtils.COLUMN_ID + " = ? ",
                new String[]{String.valueOf(bundle.getLong(EXTRA_TRANSACTION_ID,-1))}, null);

        break;
    default:
        break;
    }
    return cl;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    final int loaderId = loader.getId();
    switch (loaderId) {
    case NET_TRANS_LOADER_ID:
        if(mTransactionId != null){
            NetTrans netTrans = NetTransDbUtils.cursorToNetTrans(cursor);
            if(netTrans != null && netTrans.getStatus() != null 
                                && !netTrans.getStatus().equals(NetTrans.STATUS_PENDING)){
                EventBus.getDefault().post(new NetTransMsg(true, mTransactionId, netTrans.getMessage()));
            }
        }
        break;
    default:
        break;
    }
}

and at a runnable that runs on ExecutorService in a started service I call

mContext.getContentResolver().insert(NetTransContentProvider.CONTENT_URI_INSERT_OR_REPLACE_BY_ID, cv );

the value inserted but the loader dose not call:

@Override
public Uri insert(Uri uri, ContentValues values) {
    int uriType = sUriMatcher.match(uri);
    switch (uriType) {

    case INSERT_OR_REPLACE_BY_ID:
        mDatabase.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
        break;

    case INSERT_BY_ID:
        mDatabase.insert(TABLE_NAME, null, values);
        break;

    default:
        break;
    }

    getContext().getContentResolver().notifyChange(CONTENT_URI_ANY_OBSERVER, null);
    return null;
}
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {
        Cursor cursor = mDatabase.query(TABLE_NAME,projection,selection, selectionArgs, null, null, null);
        cursor.setNotificationUri(getContext().getContentResolver(), CONTENT_URI_ANY_OBSERVER);
        return cursor;
    }

my problem is getContext().getContentResolver().notifyChange(CONTENT_URI_ANY_OBSERVER, null); in the insert method can not make my loader restart.


UPDATE

I have created a sample project, you press the button, a new NetTrans object is created and written into database, then the thread sleeps 5000 ms and overwrites that value (to simulate network operation). but after that loader dose not restart. Where is my bug ?

Upvotes: 5

Views: 2610

Answers (2)

Justin Breitfeller
Justin Breitfeller

Reputation: 13801

If you want observers registered on CONTENT_URI_ANY_OBSERVER to be notified when a change happens on CONTENT_URI_FIND_BY_ID, you need to make sure of two things.

  • First, the CONTENT_URI_ANY_OBSERVER needs to be a parent of CONTENT_URI_FIND_BY_ID. If you think of it like folders on a file system, 'CONTENT_URI_ANY_OBSERVER' should contain `CONTENT_URI_FIND_BY_ID' in one of its sub folders.

  • Second, you must pass true for the notifyDescendants argument when registering your content observer.

There is no wild card considerations when Android attempts to find matching content observers (the link provided in the comments only applies to the UriMatcher). So, to fix your problem you should remove the /* from your CONTENT_URI_ANY_OBSERVER and it should start matching. You can see how my.url.contentprovider/NetworkTransaction is now a parent "folder" of my.url.contentprovider/NetworkTransaction/INSERT/REPLACE/ID where as before you had my.url.contentprovider/NetworkTransaction/*.

EDIT 1

After reviewing your sample project, I have found the other area where your problem lies. When you use a cursor loader, the cursor is owned by the loader. This means that you shouldn't alter it in anyway aside from just iterating through its data. In your NetTransDbUtils.cursorToNetTrans(cursor) method, you are closing your cursor which will prevent the CursorLoader from being able to effectively monitor changes to your cursor's data.

Simple answer: Don't call close cursor.close() in NetTransDbUtils.cursorToNetTrans(cursor); for this use case.

Upvotes: 6

Simas
Simas

Reputation: 44158

Replace your Uris with a simple parse:

public static final Uri CONTENT_URI_ANY_OBSERVER = Uri
        .parse(BASE_URI + "/" + TABLE_NAME);
public static final Uri CONTENT_URI_FIND_BY_ID = Uri
        .parse(BASE_URI + "/" + TABLE_NAME + "/FIND/ID");
public static final Uri CONTENT_URI_INSERT_OR_REPLACE_BY_ID = Uri
        .parse(BASE_URI + "/" + TABLE_NAME + "/INSERT/REPLACE/ID");
public static final Uri CONTENT_URI_INSERT_BY_ID = Uri
        .parse(BASE_URI + "/" + TABLE_NAME + "/INSERT/ID");

In your RestService class, use a handler for callbacks:

private Handler mHandler;

@Override
public void onCreate() {
    ...
    mHandler = new Handler();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    ...
    mHandler.post(task);
    ...
}

Upvotes: 0

Related Questions