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

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

Все примеры, которые я видел при использовании registerContentObserver(), делают это через интерфейс ContentProvider. Но курсор имеет вызов registerContentObserver(), поэтому я подумал, что, возможно, люди Android собрали некоторую глубокую магию, которая позволила бы получать обновления курсора SQLite, когда одна из строк из активного набора результатов изменилась. Либо я делаю это неправильно, либо нет такой магии. Здесь код, с которым я работаю, ожидание заключается в том, что, когда я нажимаю кнопку, чтобы обновить значение в строке №1, я получаю обратный вызов 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    
        }
    }
}
4b9b3361

Ответ 1

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

рамки/база/ядро ​​/Java/Android/базы данных /SQLite/SQLiteCursor.java рамки/база/ядро ​​/Java/Android/базы данных /AbstractCursor.java

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

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

Ответ 2

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

Вы должны определить 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); при загрузке курсора

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