20

我做了關於如何使用ContentProviders和裝載機從this tutorialcursor.setNotificationUri()用於什麼?

我怎麼看它的研究: 我們有一個ActivityListViewSimpleCursorAdapterCursorLoader。我們也執行ContentProvider

Activity中,我們可以通過點擊按鈕來調用getContentResolver().insert(URI, contentValues);

在我們實施ContentProvider,在insert()方法的末尾,我們稱之爲getContentResolver().notifyChange(URI, null);和我們CursorLoader將收到的消息,它應該重新加載數據和更新UI。另外,如果我們在SimpleCursorAdapter中使用FLAG_REGISTER_CONTENT_OBSERVER,它也會收到消息並調用其方法onContentChanged()

因此,如果我們插入,更新或刪除數據,我們的ListView將被更新。

Activity.startManagingCursor(cursor);已棄用,cursor.requery()已棄用,所以我從cursor.setNotificationUri()看不到任何練習意義。

我看着setNotificationUri()方法的源代碼,看到它在方法裏面調用mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver)CursorLoader也一樣。最後光標會收到消息,光標內將調用以下方法:

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

但是我不明白這一點。

所以我的問題是:爲什麼要在query()中調用cursor.setNotificationUri()方法我們的ContentProvider執行?

回答

26

如果你打電話Cursor.setNotificationUri(),光標會知道什麼ContentProvider的烏里它是爲創建。

CursorLoader註冊自己ForceLoadContentObserver調用setNotificationUri時(延伸ContentObserver)與ContextContentResolver的URI指定。

所以一旦ContentResolver知道URI的內容已被更改 [當你調用getContext().getContentResolver().notifyChange(uri, contentObserver);內發生這種情況ContentProviderinsert()update()delete()方法]它通知所有觀察員包括CursorLoader的ForceLoadContentObserver

ForceLoadContentObserver則標誌着Loader的mContentChanged爲真

+0

謝謝你這麼好的解釋。 –

+0

即使通過我遲到了派對, 我的問題是,這是可能的甚至是另一個應用程序正在使用此URI來查詢我們的ContentProvider? – Pankaj

14

CursorLoader寄存器觀察員光標,到URI。

請看下面的CursorLoader's source code。請注意,CursorLoadercontentObserver註冊爲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; 
      } 
     } 

Cursor需要調用方法setNotificationUri()註冊mSelfObserveruri

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

裏面的contentProviderinsertupdatedelete方法,你需要調用getContext().getContentResolver().notifyChange(uri, null);通知更改爲uri觀察員。

因此,如果您不叫cursor#setNotificationUri(),那麼CursorLoader將不會收到通知,如果數據基礎uri更改。

0

我使用一個URI作爲遊標適配器。

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

另一個類,我使用不同的URI來更改數據庫的內容。要更新我的視圖,我必須更改數據提供者的update方法的默認實現。默認實現只通知相同的URI。我必須通知另一個URI。

我最終通過調用notifyChange()兩次我的數據提供程序類,在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; 

我做了insertdelete方法相同。