web-dev-qa-db-ja.com

cursor.setNotificationUri()は何に使用されますか?

このチュートリアル からContentProvidersとローダーの使用方法を調査しました

見方:ActivityListViewSimpleCursorAdapterCursorLoaderがあります。 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);
        // ...
    }
}
_

しかし、私はこれを理解することはできません。

だから私の質問は:なぜ私たちのContentProvider実装のcursor.setNotificationUri()メソッドでquery()を呼び出す必要があるのですか?

25
anber

Cursor.setNotificationUri()を呼び出すと、カーソルは何のために作成されたかを認識しますContentProvider Uri.

CursorLoaderは、独自のForceLoadContentObserverContentObserverを拡張する)を、Contextを呼び出すときに指定したURIのContentResolversetNotificationUriに登録します。

したがって、ContentResolverがURIのコンテンツが変更されたことを認識すると変更 [これはContentProvidergetContext().getContentResolver().notifyChange(uri, contentObserver);insert()、およびupdate()メソッド内でdelete()を呼び出すと発生します] CursorLoaderのForceLoadContentObserverを含むすべてのオブザーバーに通知します。

次に、ForceLoadContentObserverは、ローダーのmContentChangedをtrueとしてマークします

33
Michael Kariv

CursorLoaderは、カーソルのオブザーバーnotをURIに登録します。

以下を調べてください CursorLoaderのソースコードCursorLoadercontentObservercursorに登録することに注意してください。

_/* 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()を呼び出して、mSelfObserveruriに登録する必要があります。

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

contentProviderinsertupdatedeleteメソッド内で、getContext().getContentResolver().notifyChange(uri, null);を呼び出してuriオブザーバー。

したがって、cursor#setNotificationUri()を呼び出さない場合、そのCursorLoaderの基になるデータが変更されても、uriは通知を受け取りません。

15
cauhn

カーソルアダプターに1つの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;
    }
}
_

別のクラスでは、I 別のURIを使用してデータベースの内容を変更します。ビューを更新するには、データプロバイダーのupdateメソッドのdefault実装を変更する必要がありました。デフォルトの実装は、同じURIのみを通知します。別のURIに通知する必要があります。

結局、データプロバイダークラスのupdateメソッドでnotifyChange()を2回呼び出しました。

_@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メソッドについても同じことをしました。

0
jgrocha