RegisterContentObserver () на исходном курсе SQLite

Все примеры, которые я видел при использовании registerContentObserver() делают это через интерфейс ContentProvider . Но курсор имеет вызов registerContentObserver() , поэтому я подумал, что, возможно, люди Android собрали некоторую глубокую магию, которая позволила бы получать обновления курсора SQLite когда одна из строк из активного набора результатов изменилась. Либо я делаю это неправильно, либо нет такой магии. Вот код, с которым я работаю, ожидая, что, когда я нажимаю на кнопку, чтобы обновить значение в строке №1, я получаю onChange() вызов onChange() . Есть идеи?

 public class MainActivity extends Activity { private static final String DATABASE_NAME = "test.db"; public static final String TAG = "dbtest"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button make = (Button)findViewById(R.id.btn_make_record); make.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyOpenHelper oh = new MyOpenHelper(v.getContext()); SQLiteDatabase wdb = oh.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put("value", String.valueOf(System.currentTimeMillis())); if (wdb.insert(MyOpenHelper.TABLE_NAME, null, cv) == -1) { Log.d(TAG, "Unable to insert row"); } else { Log.d(TAG, "Inserted row "); } wdb.close(); } }); Button update = (Button)findViewById(R.id.btn_update_record); update.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyOpenHelper oh = new MyOpenHelper(v.getContext()); SQLiteDatabase wdb = oh.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put("value", String.valueOf(System.currentTimeMillis())); int count = wdb.update(MyOpenHelper.TABLE_NAME, cv, "_id = ?", new String[] {"1"}); Log.d(TAG, "Updated " + count + " row(s)"); wdb.close(); } }); MyOpenHelper oh = new MyOpenHelper(this); SQLiteDatabase rdb = oh.getReadableDatabase(); Cursor c = rdb.query(MyOpenHelper.TABLE_NAME, null, "_id = ?", new String[] {"1"}, null, null, null); startManagingCursor(c); contentObserver = new MyContentObserver(new Handler()); c.registerContentObserver(contentObserver); } private class MyContentObserver extends ContentObserver { MyContentObserver(Handler handler) { super(handler); } public boolean deliverSelfNotifications() { return true; } public void onChange(boolean selfChange) { super.onChange(selfChange); Log.d(TAG, "Saw a change in row # 1"); } } MyContentObserver contentObserver; public class MyOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 1; private static final String TABLE_NAME = "test"; private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + "value TEXT);"; MyOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } } } 

Solutions Collecting From Web of "RegisterContentObserver () на исходном курсе SQLite"

Это действительно возможно.

Вы должны определить Uri где-нибудь (возможно, константа).

Я бы посоветовал использовать что-то вроде этого:

 public static final Uri URI_MY_TABLE = Uri.parse("sqlite://com.example.<your-app-package>/table"); 

Затем, когда вы обновляете данные базы данных, вы вызываете:

 context.getContentResolver().notifyChange(Constants.URI_MY_TABLE, null); 

И из CursorLoader, который вы пишете, делайте что-то вроде:

 final ForceLoadContentObserver observer; public MyCursorLoader(final Context context, ...) { super(context); // ... this.observer = new ForceLoadContentObserver(); } @Override public Cursor loadInBackground() { SQLiteDatabase db = this.dbHelper.getReadableDatabase(); final Cursor c = queryDatabase(db); if (c != null) { // Ensure the cursor window is filled c.getCount(); // this is to force a reload when the content change c.registerContentObserver(this.observer); // this make sure this loader will be notified when // a notifyChange is called on the URI_MY_TABLE c.setNotificationUri(getContext().getContentResolver(), Constants.URI_MY_TABLE); } return c; } 

ForceLoadContentObserver является общедоступным статическим внутренним классом внутри класса Loader , если вы используете библиотеку поддержки, это будет android.support.v4.content.Loader.ForceLoadContentObserver

В том же Loader убедитесь, что вы принудительно загружаете, если данные изменяются:

 @Override protected void onStartLoading() { if (this.cursor != null) { deliverResult(this.cursor); } // this takeContentChanged() is important! if (takeContentChanged() || this.cursor == null) { forceLoad(); } } 

Хороший старт для вашего CursorLoader, если вы не знаете, как его записать, это: использование CursorLoader без ContentProvider

Теперь ваш CursorAdapter должен быть инициализирован следующим образом:

 public MyCursorAdapter(Context context, Cursor c) { super(context, c, 0); } 

ContentResolver позаботится о том, чтобы уведомить наблюдателя об установленном вами uri.

Именно так работает ContentProvider .

Если вы не используете Loader, вы можете сделать то же самое, важные изменения:

  • Вызовите ContentResolver.notifyChange (URI, null); При изменении данных
  • Вызовите Cursor.setNotificationUri (ContentResolver, URI); При загрузке курсора

Таким образом, когда вы регистрируете наблюдателя, вы будете уведомлены о смене данных подрывания.

Нет, не так ли? Ну отличный повод, чтобы копаться в себе и понять это. Для тех, кому может быть любопытно после меня, если вы загрузите источник платформы, то соответствующие файлы, на которые нужно обратить внимание, это:

Frameworks / base / core / java / android / database / sqlite / SQLiteCursor.java frameworks / base / core / java / android / database / AbstractCursor.java

Похоже, что mContentObservable указывает на то, что разные части программы запускают кешированный результирующий набор в Cursor, а наблюдаемый – это сигнал из кэшированного набора результатов, чтобы сообщить потребителям, когда кеш был сброшен / обновлен. Он не привязывается к реализации SQLite для запуска событий, когда манипулирует базовым хранилищем данных.

Не совсем удивительно, но, учитывая документы, я подумал, может быть, я что-то упустил.