cursor.setNotificationUri()用于什么?

我研究了如何使用本教程中的 ContentProviders和Loaders

我如何看待它:我们有一个ListViewSimpleCursorAdapterCursorLoaderActivity 。 我们也实现了ContentProvider

在一个Activity我们可以调用getContentResolver().insert(URI, contentValues); 通过button点击。

在我们实现ContentProvider的end()方法中,我们调用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); // ... } } 

但我无法理解这一点。

所以我的问题是: 为什么我们应该在ContentProvider实现的query()方法中调用cursor.setNotificationUri()

如果你调用Cursor.setNotificationUri() ,Cursor会知道它创build的是什么ContentProvider Uri

CursorLoader注册自己的ForceLoadContentObserver (它扩展了ContentObserver )和ContextContentResolver用于调用setNotificationUri时指定的URI。

所以一旦ContentResolver知道URI的内容已经被改变了 ,当你调用getContext().getContentResolver().notifyChange(uri, contentObserver);时会发生这种情况getContext().getContentResolver().notifyChange(uri, contentObserver);ContentProviderinsert()update()delete()方法中],它通知所有观察者,包括CursorLoader的ForceLoadContentObserver

ForceLoadContentObserver然后将Loader的mContentChanged标记为true

CursorLoader为游标注册观察者, 而不是 URI。

看看下面的CursorLoader的源代码 。 请注意, 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()mSelfObserver注册到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; } } 

contentProviderinsertupdatedelete方法内部,需要调用getContext().getContentResolver().notifyChange(uri, null); 通知uri观察员的变化。

所以如果你不调用cursor#setNotificationUri() ,那么你的CursorLoader将不会收到通知,如果uri数据发生变化。

我使用一个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方法做了同样的事情。