J'ai fait des recherches sur la façon d'utiliser ContentProviders
et Loaders de ce tutoriel
Comment je vois les choses: Nous avons une Activity
avec ListView
, SimpleCursorAdapter
et CursorLoader
. Nous implémentons également ContentProvider
.
Dans une Activity
, nous pouvons appeler getContentResolver().insert(URI, contentValues);
par un clic de bouton.
Dans notre implémentation de ContentProvider
, à la fin de la méthode insert()
, nous appelons getContentResolver().notifyChange(URI, null);
et notre CursorLoader
recevra un message indiquant qu'il devrait recharger les données et mettre à jour l'interface utilisateur. De plus, si nous utilisons FLAG_REGISTER_CONTENT_OBSERVER
dans SimpleCursorAdapter
, il recevra également un message et sa méthode onContentChanged()
sera appelée.
Donc, notre ListView sera mis à jour si nous insérons, mettons à jour ou supprimons des données.
Activity.startManagingCursor(cursor);
est obsolète, cursor.requery()
obsolète, je ne vois donc aucun sens à la pratique de cursor.setNotificationUri()
.
J'ai examiné le code source de la méthode setNotificationUri()
et j'ai constaté qu'il appelle mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver)
dans la méthode. De plus, CursorLoader
fait de même. Enfin, le curseur recevra un message et la méthode suivante sera appelée dans le curseur:
protected void onChange(boolean selfChange) {
synchronized (mSelfObserverLock) {
mContentObservable.dispatchChange(selfChange, null);
// ...
}
}
Mais je ne peux pas donner un sens à cela.
Ma question est donc la suivante: pourquoi devrions-nous appeler la méthode cursor.setNotificationUri()
dans la méthode query()
de notre implémentation ContentProvider
?
Si vous appelez Cursor.setNotificationUri()
, Cursor saura pourquoi ContentProvider Uri il a été créé.
CursorLoader
enregistre sa propre ForceLoadContentObserver
(qui étend ContentObserver
) avec la Context
's ContentResolver
pour l'URI que vous avez spécifié lors de l'appel de setNotificationUri
.
Donc, une fois que ContentResolver
sait que le contenu de l'URI a été modifié [cela se produit lorsque vous appelez getContext().getContentResolver().notifyChange(uri, contentObserver);
dans les méthodes insert()
, update()
et delete()
de ContentProvider
], il en informe tous les observateurs, y compris les variables ForceLoadContentObserver
de CursorLoader.
ForceLoadContentObserver
marque alors le mContentChanged de Loader comme vrai
CursorLoader
enregistre l'observateur du curseur, not dans l'URI.
Regardez dans le code source de CursorLoader ci-dessous. Notez que CursorLoader
enregistre contentObserver
dans cursor
.
/* Runs on a worker thread */
@Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
La Cursor
doit appeler la méthode setNotificationUri()
pour enregistrer mSelfObserver
à la uri
.
//AbstractCursor.Java
public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) {
synchronized (mSelfObserverLock) {
mNotifyUri = notifyUri;
mContentResolver = cr;
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
mSelfObserver = new SelfContentObserver(this);
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle); // register observer to the uri
mSelfObserverRegistered = true;
}
}
Dans les méthodes contentProvider
's insert
, update
, delete
, vous devez appeler getContext().getContentResolver().notifyChange(uri, null);
pour notifier le changement aux observateurs uri
.
Par conséquent, si vous n'appelez pas cursor#setNotificationUri()
, votre CursorLoader
ne recevra aucune notification si les données sous-jacentes à cette uri
sont modifiées.
J'utilise un URI pour l'adaptateur de curseur.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = new Bundle();
Uri uri = TemperatureContract.SensorEntry.buildSensorID0AddressUri(mDeviceAddress);
args.putParcelable("URI", uri);
getSupportLoaderManager().initLoader(0, args, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (args != null) {
Uri mUri = args.getParcelable("URI");
return new CursorLoader(this,
mUri,
null, // projection
null, // selection
null, // selectionArgs
null); // sortOrder
} else {
return null;
}
}
Sur une autre classe, je utilise un autre URI pour modifier le contenu de la base de données . Pour que mon vue soit mise à jour, je devais changer l'implémentation default de la méthode update
du fournisseur de données. L'implémentation par défaut ne notifie que le même URI. Je dois notifier un autre URI.
J'ai fini par appeler deux fois la notifyChange()
sur ma classe de fournisseur de données, sur la méthode update
:
@Override
public int update(
Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsUpdated;
switch (match) {
case ...:
break;
case SENSOR_BY_ID_AND_ADDRESS:
String sensorId = TemperatureContract.SensorEntry.getSensorIdFromUri(uri);
String sensorAddress = TemperatureContract.SensorEntry.getSensorAddressFromUri(uri);
rowsUpdated = db.update(
TemperatureContract.SensorEntry.TABLE_NAME, values, "sensorid = ? AND address = ?", new String[]{sensorId, sensorAddress});
if (rowsUpdated != 0) {
Uri otheruri = TemperatureContract.SensorEntry.buildSensorID0AddressUri(sensorAddress);
getContext().getContentResolver().notifyChange(otheruri, null);
}
break;
case ...:
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
J'ai fait la même chose pour les méthodes insert
et delete
.