Для чего используется cursor.setNotificationUri()?

Я изучил, как использовать ContentProviders и загрузчики из этого руководства.

Как я это вижу: у нас есть Activity с ListView, SimpleCursorAdapter и CursorLoader. Мы также реализуем ContentProvider.

В Activity мы можем вызвать getContentResolver().insert(URI, contentValues); нажатием кнопки.

В нашей реализации ContentProvider в конце метода insert() мы вызываем getContentResolver().notifyChange(URI, null);, и наш CursorLoader получит сообщение о том, что он должен перезагрузить данные и обновить пользовательский интерфейс. Также, если мы используем FLAG_REGISTER_CONTENT_OBSERVER в SimpleCursorAdapter, он также получит сообщение и будет вызван его метод 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);
        // ...
    }
}

Но я не могу понять это.

Итак, мой вопрос: почему мы должны вызывать cursor.setNotificationUri() в методе query() нашей реализации ContentProvider?


person anber    schedule 07.02.2014    source источник


Ответы (3)


Если вы вызовете Cursor.setNotificationUri(), Cursor будет знать, для какого ContentProvider Uri он был создан.

CursorLoader регистрирует свой собственный ForceLoadContentObserver (который расширяет ContentObserver) с ContentResolver Context для URI, который вы указали при вызове setNotificationUri.

Итак, как только ContentResolver узнает, что содержимое URI было изменено [это происходит, когда вы вызываете getContext().getContentResolver().notifyChange(uri, contentObserver); внутри методов insert(), update() и delete() ContentProvider], он уведомляет всех наблюдателей, включая ForceLoadContentObserver CursorLoader.

ForceLoadContentObserver затем помечает mContentChanged загрузчика как true

person Michael Kariv    schedule 16.02.2014
comment
Несмотря на то, что я опаздываю на вечеринку, у меня возникает вопрос, возможно ли это даже для другого приложения, которое использует этот URI для запроса нашего ContentProvider? - person Pankaj; 02.02.2018

CursorLoader регистрирует наблюдателя для курсора, не для URI.

Посмотрите Исходный код CursorLoader ниже. Обратите внимание, что 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;
        }
    }

Внутри методов insert, update, delete contentProvider вам нужно вызвать getContext().getContentResolver().notifyChange(uri, null);, чтобы уведомить об изменении наблюдателей uri.

Поэтому, если вы не позвоните cursor#setNotificationUri(), ваш CursorLoader не получит уведомления, если данные, лежащие в основе этого uri, изменятся.

person cauhn    schedule 26.09.2015

Я использую один 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.

person jgrocha    schedule 19.04.2017