20

Ich habe Forschung auf, wie aus this tutorialWofür wird cursor.setNotificationUri() verwendet?

ContentProviders und Lader zu verwenden, wie ich es sehe: Wir haben ein Activity mit ListView, SimpleCursorAdapter und CursorLoader. Wir implementieren auch ContentProvider.

In einem Activity können wir getContentResolver().insert(URI, contentValues); per Knopfdruck aufrufen.

In unserer Implementierung von ContentProvider, am Ende der insert() Methode nennen wir getContentResolver().notifyChange(URI, null); und unsere CursorLoader wird Meldung, die es Daten und Update UI neu laden sollte. Auch wenn wir FLAG_REGISTER_CONTENT_OBSERVER in SimpleCursorAdapter verwenden, wird es auch Nachricht erhalten und seine Methode onContentChanged() wird aufgerufen.

So wird unser ListView aktualisiert, wenn wir Daten einfügen, aktualisieren oder löschen.

Activity.startManagingCursor(cursor); ist veraltet, cursor.requery() veraltet, so sehe ich keine Praxis Sinn von cursor.setNotificationUri().

Ich schaute in setNotificationUri() Methode Quellcode und sah, dass es mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver) innerhalb der Methode ruft. Auch CursorLoader macht das gleiche. Schließlich erhält der Cursor eine Nachricht und die folgende Methode wird im Cursor aufgerufen:

protected void onChange(boolean selfChange) { 
    synchronized (mSelfObserverLock) { 
     mContentObservable.dispatchChange(selfChange, null); 
     // ... 
    } 
} 

Aber ich kann keinen Sinn daraus machen.

Also meine Frage ist: warum sollten wir cursor.setNotificationUri() in query() Methode unserer ContentProvider Implementierung nennen?

Antwort

26

Wenn Sie Cursor.setNotificationUri() nennen, Cursor wird wissen, was Contentprovider Uri es wurde erstellt.

CursorLoader registriert seine eigene ForceLoadContentObserver mit der Context ‚s ContentResolver für den URI (die ContentObserver erstreckt) angegeben Sie, wenn setNotificationUri aufrufen.

So einmal, dass ContentResolver weiß, dass URI Inhalt wurde geändert [dies geschieht, wenn Sie getContext().getContentResolver().notifyChange(uri, contentObserver); innerhalb ContentProvider ‚nennen s insert(), update() und delete() Methoden] benachrichtigt er alle Beobachter einschließlich CursorLoader des ForceLoadContentObserver.

ForceLoadContentObserver markiert dann Loader des mContentChanged als wahre

+0

Vielen Dank für solch eine wunderbare Erklärung. –

+0

Auch wenn ich zu spät zur Party bin, meine Fragen sind, ist dies sogar für eine andere Anwendung möglich, die diese URI verwendet, um unseren ContentProvider abzufragen? – Pankaj

14

CursorLoader Register Beobachter für den Cursor, nicht an den URI.

Schauen Sie sich unten CursorLoader's source code an. Beachten Sie, dass CursorLoadercontentObserver an die cursor anmeldet.

/* 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; 
      } 
     } 

Die Cursor Bedürfnisse Methode aufzurufen setNotificationUri()mSelfObserver zum uri zu registrieren.

//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; 
     } 
    } 

Im Inneren des contentProvider ‚s insert, update, delete Methoden, müssen Sie getContext().getContentResolver().notifyChange(uri, null); anrufen Änderung der uri Beobachter zu benachrichtigen.

Wenn Sie also nicht cursor#setNotificationUri() anrufen, wird Ihre CursorLoader keine Benachrichtigung erhalten, wenn sich die Daten, die dem uri zugrunde liegen, ändern.

0

Ich verwende einen URI für den Cursor-Adapter.

@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; 
    } 
} 

Auf einer anderen Klasse, ich einen anderen URI verwenden die Datenbankinhalte zu ändern. Um meine Ansicht zu aktualisieren, musste ich die Standard Implementierung der update Methode des Datenanbieters ändern. Die Standardimplementierung benachrichtigt nur denselben URI. Ich muss einen anderen URI benachrichtigen.

landete ich durch die notifyChange() zweimal auf meine Daten-Provider-Klasse aufrufen, auf der update Methode:

@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; 

habe ich das gleiche für die insert und delete Methoden.