【问题标题】:What is cursor.setNotificationUri() used for?cursor.setNotificationUri() 是做什么用的?
【发布时间】:2014-03-04 15:02:29
【问题描述】:

我研究了如何使用ContentProviders 和来自this tutorial 的加载器

我是怎么看的: 我们有一个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);
        // ...
    }
}

但我无法理解这一点。

所以我的问题是:我们为什么要在 query() 方法的 ContentProvider 实现中调用 cursor.setNotificationUri()

【问题讨论】:

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


    【解决方案1】:

    如果您调用Cursor.setNotificationUri(),Cursor 将知道它是为什么而创建的ContentProvider Uri

    CursorLoader 使用ContextContentResolver 为您在调用setNotificationUri 时指定的URI 注册自己的ForceLoadContentObserver(扩展ContentObserver)。

    所以一旦 ContentResolver 知道 URI 的内容已更改 [当您在 ContentProviderinsert()update()delete() 方法中调用 getContext().getContentResolver().notifyChange(uri, contentObserver); 时,就会发生这种情况] 它会通知所有的观察者,包括 CursorLoader 的ForceLoadContentObserver

    ForceLoadContentObserver 然后将 Loader 的 mContentChanged 标记为 true

    【讨论】:

    • 即使我迟到了,我的问题是,即使对于使用此 URI 查询我们的 ContentProvider 的另一个应用程序,这是否可能?
    【解决方案2】:

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

    contentProviderinsertupdatedelete 方法中,您需要调用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;
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-29
        • 2011-08-24
        • 2023-04-03
        • 2012-05-19
        • 2011-10-25
        • 2015-07-17
        • 2014-02-26
        • 2012-06-29
        相关资源
        最近更新 更多