Подтвердить что ты не робот

Что такое cursor.setNotificationUri()?

Я изучил, как использовать ContentProviders и Loaders этот учебник

Как я вижу это: Мы имеем 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?

4b9b3361

Ответ 1

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

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

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

ForceLoadContentObserver затем отмечает Loader mContentChanged как true

Ответ 2

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;
        }
    }

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

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

Ответ 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. Реализация по умолчанию уведомляет только тот же 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.