web-dev-qa-db-fra.com

A quoi sert cursor.setNotificationUri ()?

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?

23
anber

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

29
Michael Kariv

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.

14
cauhn

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 insertet delete.

0
jgrocha