SyncAdapter, ContentProvider, Entity-Flags.

  • Antworten:1
  • Bentwortet
Gelöschter Account
  • Forum-Beiträge: 10

19.05.2013, 13:27:58 via Website

Hallo.

In meinem aktuellen Projekt kommuniziert eine Android-Anwendung mit einem REST-Service, um Daten zu synchronisieren. Ich habe bereits einen ContentProvider zum Zugriff auf die lokal gespiegelten Daten, sowie einen SyncAdapter (und was noch alles dazu gehört...). Das funktioniert soweit auch ganz gut.

Ich habe jetzt allerdings eine eher konzeptionelle Frage und würde mich über eure Erfahrungen oder gesammelte Best Practices dazu freuen :)

Jedes Datenobjekt, welches über den WebService synchronisiert werden soll, verfügt über folgende Flags: Created, Changed, Deleted. Diese geben an, was nach der letzten Synchronisierung mit dem Datenobjekt passiert ist. Created bedeuted beispielsweise, dass das Datenobjekt nicht auf dem Server existiert und daher mit einem POST-Request erstellt werden soll. Deleted bedeutet, dass das Objekt auf dem Server existiert, aber lokal gelöscht wurde. Solche Objekte sollen natürlich nicht mehr im UI angezeigt werden.

Die Frage ist nun, wer für die Verwaltung dieser Flags verantwortlich ist. Meine erste Idee (an Anlehnung an diese Präsentation: www[dot]youtube[dot]com/watch?v=xHXn3Kg2IQE) war, dies im ContentProvider zu erledigen. Soll heißen, löschen im Provider löscht nicht wirklich, sondern setzt ein Flag. Eine query gibt stets nur Objekte zurück, die nicht als gelöscht markiert sind, update setzt das Changed-Flag. Aus Sicht des UI ist das sehr schön, ich muss nicht einmal wissen, dass der ContentProvider intern mit Flags arbeitet, der ContentProvider erfüllt seine Aufgabe und abstrahiert vollständig von der hinter ihm liegenden Datenbank.

Und hier gehts dann los: Der SyncAdapter arbeitet ebenfalls mit dem gleichen ContentProvider, braucht aber zum Synchronisieren die Flag-Informationen und auch die Möglichkeit, Objekte endgültig zu löschen. Hier müsste ich den ContentProvider irgendwie in einen anderen 'Modus' versetzen können, das geht aber meiner Meinung nach nicht.

Zwei verschiedene ContentProvider sind leider nicht drinn, dann bekomme ich Probleme mit parallelen Datenbankanfragen und werde nicht bei Änderungen durch den SyncAdapter benachrichtigt.

Die einzige Lösung, die ich sehe: Der ContentProvider ignoriert die Flags. Wenn das UI Objekte über den ContentProvider erstellt oder bearbeitet, werden vom Code des UI auch gleich die Flags gesetzt. Das will ich aber UNBEDINGT vermeiden. Der Anwendung selbst soll es ja egal sein, wie die Daten hinter dem ContentProvider organisiert sind. Das war auch einer der wichtigen Punkte aus der oben verlinkten Präsentation.

Wie macht man sowas richtig? Danke für's Lesen und mögliche Hinweise/Antworten :)

Grüße,
Jonas

— geändert am 19.05.2013, 17:54:53

Antworten
Gelöschter Account
  • Forum-Beiträge: 10

29.05.2013, 20:29:10 via Website

Hier meine Lösung für das Problem, falls nochmal jemand darüber stolpern sollte:

Der ContentProvider unterstützt zwei Modi:
  1. Der erste Modus ist für die Anwendung selbst. In diesem Modus wird nicht gelöscht, sondern das deleted-Flag gesetzt. Zurückgegeben werden nur nicht als markiert gelöschte Objekte, etc.
  2. Der zweite Modus ist für den SyncAdapter und ignoriert die created/changed/deleted Flags.

Der Modus wird über die URI beim Aufruf des ContentProviders erledigt, zB
1content://AUTHORITY/item/35?sync=true

Zum Parsen / Matchen der URIs hatte ich bereits die Hilfsklasse UriMatcher benutzt. Diese ignoriert Query-Parameter. Query-Parameter können mit der Utility-Klasse UrlQuerySanitizer ausgewertet werden:

1mUrlQuerySanitizer.parseUrl(uri.toString());
2mUrlQuerySanitizer.getValue("sync");

Der ContentProvider entscheidet dann, wie er mit den Flags umgehen soll.

Als Extra zum einfachen Erstellen von URIs habe ich mir dann noch folgende Hilfsmethoden angelegt:

1public static Uri item(long id, boolean sync) {
2 Builder uri = new Uri.Builder().scheme("content").authority(AUTHORITY).path("item").appendPath(String.valueOf(id));
3 if (sync) {
4 uri.appendQueryParameter("sync", "true");
5 }
6 return uri.build();
7}

Antworten