我研究了如何使用this tutorial
中的ContentProviders
和加载器
我怎么看:
我们有Activity
ListView
,SimpleCursorAdapter
和CursorLoader
。我们还实施了ContentProvider
。
在Activity
中,我们可以通过点击按钮调用getContentResolver().insert(URI, contentValues);
。
在我们ContentProvider
的实现中,在insert()
方法的末尾,我们调用getContentResolver().notifyChange(URI, null);
,我们的CursorLoader
将收到消息,指出它应该重新加载数据并更新UI。此外,如果我们在FLAG_REGISTER_CONTENT_OBSERVER
中使用SimpleCursorAdapter
,它也会收到消息并调用其方法onContentChanged()
。
因此,如果我们插入,更新或删除数据,我们的ListView将会更新。
Activity.startManagingCursor(cursor);
已被弃用,cursor.requery()
已被弃用,因此我认为cursor.setNotificationUri()
没有任何实践意义。
我查看了setNotificationUri()
方法的源代码,发现它在方法中调用了mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver)
。同样CursorLoader
也是如此。最后,游标将接收消息,并在Cursor:
protected void onChange(boolean selfChange) {
synchronized (mSelfObserverLock) {
mContentObservable.dispatchChange(selfChange, null);
// ...
}
}
但我无法理解这一点。
所以我的问题是:我们为什么要在cursor.setNotificationUri()
实施的query()
方法中调用ContentProvider
?
答案 0 :(得分:29)
如果您致电Cursor.setNotificationUri()
,Cursor将知道为其创建的 ContentProvider Uri 。
CursorLoader
在调用{{1}时为您指定的URI注册自己的ForceLoadContentObserver
(扩展ContentObserver
)Context
的{{1}} }。
一旦ContentResolver
知道URI的内容已经更改 [当您在setNotificationUri
的{{1}}内拨打ContentResolver
时会发生这种情况,它会通知所有观察者,包括CursorLoader的getContext().getContentResolver().notifyChange(uri, contentObserver);
。
ContentProvider
然后将Loader的mContentChanged标记为true
答案 1 :(得分:14)
CursorLoader
向游标的观察者注册,不到URI。
请查看下面的CursorLoader's source code。请注意,CursorLoader
将contentObserver
注册到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;
}
}
在contentProvider
insert
,update
,delete
方法中,您需要致电getContext().getContentResolver().notifyChange(uri, null);
以通知对{uri
的更改1}}观察员。
因此,如果您不打电话给cursor#setNotificationUri()
,那么如果基础CursorLoader
的数据发生变化,您的uri
将不会收到通知。
答案 2 :(得分: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;
我对insert
和delete
方法做了同样的事情。