2011-06-24 11 views
16

Wszystkie przykłady, które widziałem podczas korzystania z registerContentObserver(), czynią to poprzez interfejs ContentProvider. Ale Cursor ma wywołanie registerContentObserver(), więc doszedłem do wniosku, że może ludzie z Androidem stworzyli trochę głębokiej magii, która pozwoliłaby uzyskać aktualizacje na kursorze SQLite, gdy zmieni się jeden z wierszy z aktywnego zestawu wyników. Albo robię to źle, albo nie ma takiej magii. Oto kod, nad którym pracuję, oczekując, że po kliknięciu przycisku, aby zaktualizować wartość w wierszu # 1, otrzymam wywołanie zwrotne onChange(). Jakieś pomysły?registerContentObserver() na surowym SQLite Cursor

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  
     } 
    } 
} 
+0

Hej człowieku, przyjęta odpowiedź jest błędna, wiem, że to twoja odpowiedź, ale nie jest właściwa. –

Odpowiedz

15

Brak użytkowników huh? Cóż, jest to doskonała wymówka, żeby się w siebie zakopać i zrozumieć. Dla tych, którzy mogą być ciekawi mnie, jeśli pobierzesz źródło platformy, odpowiednie pliki do obejrzenia to:

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

Wygląda na to, że funkcja mContentObservable służy do sygnalizowania różnych części programu, które uruchamiają buforowany zestaw wyników w kursorze, a obserwowalny jest sygnałem z zestawu wyników zapisanych w pamięci podręcznej. powiadomić konsumentów, kiedy pamięć podręczna została zrzucona/zaktualizowana. Nie wiąże się z implementacją SQLite w celu wywoływania zdarzeń podczas manipulowania bazowym magazynem danych.

Niezupełnie zaskakujące, ale biorąc pod uwagę dokumenty, pomyślałem, że może czegoś mi brakowało.

+0

Czego nie rozumiem, to co sprawia, że ​​ContentProvider jest wyjątkowy? Jeśli ContentProvider jest wspierany przez SQLiteDatabase, w jaki sposób jego registerContentObserver() robi to, co trzeba? – jfritz42

+1

Podłącza się przez resolver zawartości. Kiedy dostawca wywoła funkcję notifyChange dla programu rozpoznawania nazw uri, program rozpoznawania nazw zadba o oddzwonienie do wszystkich obserwatorów. – mikerowehl

+0

@ jfritz42 a "ContentProvider" jest odpowiedzialny za czytanie i pisanie. Tak więc, nawet jeśli jest on wspierany przez bazę danych SQLite, może wywoływać dowolne detektory po kontynuowaniu operacji zapisu. – Felix

20

To jest rzeczywiście możliwe.

Musisz zdefiniować gdzieś Uri (może być stała).

radziłbym użyć czegoś takiego:

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

Następnie po aktualizacji danych bazy danych połączenia:

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

I od CursorLoader piszesz zrobić coś takiego:

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

The ForceLoadContentObserver jest publiczną statyczną klasą wewnętrzną wewnątrz klasy Loader, jeśli używasz biblioteki Support, będzie to android.support.v4.content.Loader.ForceLoadContentObserver

W tym samym Loader upewnij się forceReload jeśli zmiana danych:

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

Dobry start dla Twojego CursorLoader jeśli nie wiem jak to napisać to: CursorLoader usage without ContentProvider

Teraz należy zainicjować Twój CursorAdapter w następujący sposób:

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

ContentResolver zajmie powiadamiając obserwatora na URI skonfigurowanym.

Dokładnie tak działa ContentProvider.

Jeśli nie używasz Loader można robić te same rzeczy, ważne zmiany to:

  • połączeń ContentResolver.notifyChange (URI, null); po zmianie danych
  • Zadzwoń pod nr Cursor.setNotificationUri (ContentResolver, URI); po załadowaniu kursora

W ten sposób po zarejestrowaniu obserwatora użytkownik zostanie powiadomiony o zmianie danych podrzędnych.

+1

Działa całkiem nieźle! DZIĘKI Daniele – Shatazone

+0

Niesamowite rozwiązanie Daniele !! Czy mogę zrobić to samo na iOS? –

+0

Naprawdę wątpię, że Geet Choubey, ładowarki i ContentResolver są częścią systemu Android :-) –

Powiązane problemy