cursor.setNotificationUri()用于什么?

时间:2014-02-07 09:19:22

标签: android database cursor android-contentprovider android-contentresolver

我研究了如何使用this tutorial

中的ContentProviders和加载器

我怎么看: 我们有Activity ListViewSimpleCursorAdapterCursorLoader。我们还实施了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

3 个答案:

答案 0 :(得分:29)

如果您致电Cursor.setNotificationUri(),Cursor将知道为其创建的 ContentProvider Uri

CursorLoader在调用{{1}时为您指定的URI注册自己的ForceLoadContentObserver(扩展ContentObserverContext的{​​{1}} }。

一旦ContentResolver知道URI的内容已经更改 [当您在setNotificationUri的{​​{1}}内拨打ContentResolver时会发生这种情况,它会通知所有观察者,包括CursorLoader的getContext().getContentResolver().notifyChange(uri, contentObserver);

ContentProvider然后将Loader的mContentChanged标记为true

答案 1 :(得分: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()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 insertupdatedelete方法中,您需要致电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;

我对insertdelete方法做了同样的事情。