cursor.setNotificationUri() 是做啥用的?
Posted
技术标签:
【中文标题】cursor.setNotificationUri() 是做啥用的?【英文标题】:What is cursor.setNotificationUri() used for?cursor.setNotificationUri() 是做什么用的? 【发布时间】:2014-03-04 15:02:29 【问题描述】:我研究了如何使用ContentProviders
和来自this tutorial 的加载器
我是怎么看的:
我们有一个Activity
和ListView
、SimpleCursorAdapter
和CursorLoader
。我们还实现了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
也一样。最后 cursor 将收到消息,并在 Cursor 内部调用以下方法:
protected void onChange(boolean selfChange)
synchronized (mSelfObserverLock)
mContentObservable.dispatchChange(selfChange, null);
// ...
但我无法理解这一点。
所以我的问题是:我们为什么要在 query()
方法的 ContentProvider
实现中调用 cursor.setNotificationUri()
?
【问题讨论】:
【参考方案1】:如果您调用Cursor.setNotificationUri()
,Cursor 将知道它是为什么而创建的ContentProvider Uri。
CursorLoader
使用Context
的ContentResolver
为您在调用setNotificationUri
时指定的URI 注册自己的ForceLoadContentObserver
(扩展ContentObserver
)。
所以一旦 ContentResolver
知道 URI 的内容已更改 [当您在 ContentProvider
的 insert()
、update()
和 delete()
方法中调用 getContext().getContentResolver().notifyChange(uri, contentObserver);
时,就会发生这种情况] 它会通知所有的观察者,包括 CursorLoader 的ForceLoadContentObserver
。
ForceLoadContentObserver
然后将 Loader 的 mContentChanged 标记为 true
【讨论】:
即使我迟到了,我的问题是,即使对于使用此 URI 查询我们的 ContentProvider 的另一个应用程序,这是否可能?【参考方案2】: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
观察者的更改。
因此,如果您不致电cursor#setNotificationUri()
,那么如果uri
的基础数据发生变化,您的CursorLoader
将不会收到通知。
【讨论】:
【参考方案3】:我为光标适配器使用一个 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
方法的 default 实现。默认实现只通知相同的 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
方法做了同样的事情。
【讨论】:
以上是关于cursor.setNotificationUri() 是做啥用的?的主要内容,如果未能解决你的问题,请参考以下文章