Ich versuche, das Content-Provider-Sync-Adapter-Muster zu implementieren, wie unter beschrieben Google IO – Folie 26. Mein Inhaltsanbieter funktioniert, und meine Synchronisierung funktioniert, wenn ich sie über die Dev Tools Sync Tester-Anwendung auslöse. Wenn ich jedoch ContentResolver.requestSync(account, Authority, bundle) von meinem ContentProvider aus aufrufe, wird meine Synchronisierung nie ausgelöst.
ContentResolver.requestSync(
account,
AUTHORITY,
new Bundle());
Bearbeiten — Manifest-Snippet hinzugefügt Meine Manifest-XML enthält:
<service
android:name=".sync.SyncService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
–Bearbeiten
Meine mit meinem Synchronisierungsdienst verknüpfte syncadapter.xml enthält:
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="AUTHORITY"
android:accountType="myaccounttype"
android:supportsUploading="true"
/>
Nicht sicher, welcher andere Code nützlich wäre. Das an requestSync übergebene Konto ist “myaccounttype” und die an den Aufruf übergebene AUTORITÄT stimmt mit meiner syc-Adapter-XML überein.
Ist ContentResolver.requestSync der richtige Weg, um eine Synchronisierung anzufordern? Es sieht so aus, als ob sich das Sync-Tester-Tool direkt an den Dienst bindet und die Synchronisierung startet, aber das scheint den Zweck der Integration in die Sync-Architektur zunichte zu machen.
Wenn dies der richtige Weg ist, um eine Synchronisierung anzufordern, warum sollte dann der Synchronisierungstester funktionieren, aber nicht mein Aufruf von ContentResolver.requestSync? Gibt es etwas, das ich im Bündel weitergeben muss?
Ich teste im Emulator auf Geräten mit 2.1 und 2.2.
Berufung requestSync()
funktioniert nur mit einem {Account, ContentAuthority}-Paar, das dem System bekannt ist. Ihre App muss eine Reihe von Schritten durchlaufen, um Android mitzuteilen, dass Sie eine bestimmte Art von Inhalten mit einer bestimmten Art von Konto synchronisieren können. Dies geschieht im AndroidManifest.
1. Benachrichtigen Sie Android, dass Ihr Anwendungspaket die Synchronisierung bereitstellt
Zunächst müssen Sie in AndroidManifest.xml angeben, dass Sie einen Synchronisierungsdienst haben:
<service android:name=".sync.mySyncService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_myapp" />
</service>
Das Namensattribut der <service>
tag ist der Name Ihrer Klasse, um die Synchronisierung herzustellen … Ich werde gleich darauf eingehen.
Das Festlegen von exported true macht es für andere Komponenten sichtbar (muss so sein ContentResolver
kann es anrufen).
Der Intent-Filter ermöglicht es, einen Intent abzufangen, der eine Synchronisierung anfordert. (Dies Intent
kommt von ContentResolver
wenn du anrufst ContentResolver.requestSync()
oder verwandte Scheduling-Methoden.)
Der <meta-data>
-Tag wird weiter unten besprochen.
2. Stellen Sie Android einen Dienst zur Verfügung, mit dem Sie Ihren SyncAdapter finden können
Also die Klasse selbst … Hier ist ein Beispiel:
public class mySyncService extends Service {
private static mySyncAdapter mSyncAdapter = null;
public SyncService() {
super();
}
@Override
public void onCreate() {
super.onCreate();
if (mSyncAdapter == null) {
mSyncAdapter = new mySyncAdapter(getApplicationContext(), true);
}
}
@Override
public IBinder onBind(Intent arg0) {
return mSyncAdapter.getSyncAdapterBinder();
}
}
Ihre Klasse muss erweitert werden Service
oder eine ihrer Unterklassen implementieren muss public IBinder onBind(Intent)
und muss a zurückgeben SyncAdapterBinder
Wenn das aufgerufen wird … Sie benötigen eine Variable vom Typ AbstractThreadedSyncAdapter
. Wie Sie sehen können, ist das so ziemlich alles in dieser Klasse. Der einzige Grund dafür ist, einen Dienst bereitzustellen, der eine Standardschnittstelle für Android bietet, um Ihre Klasse abzufragen, was Ihre ist SyncAdapter
selbst ist.
3. Geben Sie a class SyncAdapter
um die Synchronisierung tatsächlich durchzuführen.
mySyncAdapter ist der Ort, an dem die eigentliche Synchronisierungslogik selbst gespeichert ist. Es ist onPerformSync()
-Methode wird aufgerufen, wenn es Zeit für die Synchronisierung ist. Ich nehme an, Sie haben dies bereits an Ort und Stelle.
4. Richten Sie eine Bindung zwischen einem Kontotyp und einer Inhaltsautorität ein
Wenn ich noch einmal auf AndroidManifest zurückblicke, ist das seltsam <meta-data>
-Tag in unserem Service ist das Schlüsselelement, das die Bindung zwischen einer ContentAuthority und einem Konto herstellt. Es verweist extern auf eine andere XML-Datei (nennen Sie es, wie Sie möchten, etwas, das für Ihre App relevant ist.) Schauen wir uns sync_myapp.xml an:
<?xml version="1.0" encoding="utf-8" ?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
android:accountType="com.google"
android:userVisible="true" />
Okay, also was macht das? Es teilt Android mit, dass der von uns definierte Synchronisierungsadapter (die Klasse, die im Namenselement der <service>
-Tag, das die enthält <meta-data>
-Tag, das auf diese Datei verweist …) synchronisiert Kontakte mit einem Konto im com.google-Stil.
Alle Ihre contentAuthority-Strings müssen übereinstimmen und mit dem übereinstimmen, was Sie synchronisieren – Dies sollte ein String sein, den Sie definieren, wenn Sie Ihre eigene Datenbank erstellen, oder Sie sollten einige vorhandene Geräte-Strings verwenden, wenn Sie bekannte Synchronisierungen durchführen Datentypen (wie Kontakte oder Kalenderereignisse oder was Sie haben.) Das obige (“com.android.contacts”) ist zufällig die ContentAuthority-Zeichenfolge für Kontakttypdaten (Überraschung, Überraschung.)
accountType muss auch mit einem dieser bekannten Kontotypen übereinstimmen, die bereits eingegeben wurden, oder es muss mit einem übereinstimmen, den Sie erstellen. Auch hier ist “com.google” die definierte Zeichenfolge, die … Anmeldeinformationen für Konten im Stil von google.com identifiziert (auch dies sollte keine Überraschung sein).
5. Aktivieren Sie die Synchronisierung für ein bestimmtes Konto/ContentAuthority-Paar
Schließlich muss die Synchronisierung aktiviert werden. Sie können dies auf der Seite „Konten & Synchronisierung“ im Steuerungsfeld tun, indem Sie zu Ihrer App gehen und das Kontrollkästchen neben Ihrer App innerhalb des übereinstimmenden Kontos aktivieren. Alternativ können Sie dies in einem Einrichtungscode in Ihrer App tun:
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
Damit die Synchronisierung stattfinden kann, muss Ihr Konto/Autoritätspaar für die Synchronisierung aktiviert sein (wie oben). und das gesamte globale Sync-Flag auf dem System muss gesetzt sein, und Das Gerät muss über eine Netzwerkverbindung verfügen.
Wenn Ihre Konto-/Autoritätssynchronisierung oder die globale Synchronisierung deaktiviert sind, hat das Aufrufen von RequestSync() eine Auswirkung — Es setzt ein Flag, dass die Synchronisierung angefordert wurde, und wird ausgeführt, sobald die Synchronisierung aktiviert ist.
Auch pro mgv, Einstellung ContentResolver.SYNC_EXTRAS_MANUAL
auf true im Extras-Bundle Ihres RequestSync wird Android auffordern, eine Synchronisierung zu erzwingen, auch wenn die globale Synchronisierung deaktiviert ist (seien Sie hier respektvoll gegenüber Ihrem Benutzer!).
Schließlich können Sie eine regelmäßige geplante Synchronisierung einrichten, wiederum mit ContentResolver-Funktionen.
6. Berücksichtigen Sie die Auswirkungen mehrerer Konten
Es ist möglich, mehr als ein Konto desselben Typs zu haben (zwei @gmail.com-Konten, die auf einem Gerät eingerichtet sind, oder zwei Facebook-Konten oder zwei Twitter-Konten usw.). Sie sollten die Auswirkungen auf die Anwendung berücksichtigen. .. Wenn Sie zwei Konten haben, möchten Sie wahrscheinlich nicht versuchen, beide mit denselben Datenbanktabellen zu synchronisieren. Möglicherweise müssen Sie angeben, dass jeweils nur einer aktiv sein kann, und die Tabellen leeren und neu synchronisieren, wenn Sie das Konto wechseln. (über eine Eigenschaftsseite, die abfragt, welche Konten vorhanden sind). Vielleicht erstellen Sie für jedes Konto eine andere Datenbank, vielleicht verschiedene Tabellen, vielleicht eine Schlüsselspalte in jeder Tabelle. Alles anwendungsspezifisch und eine Überlegung wert. ContentResolver.setIsSyncable(Account account, String authority, int syncable)
könnte hier interessant sein. setSyncAutomatically()
steuert, ob es sich um ein Konto/Autorität-Paar handelt überprüft oder ungeprüftwohingegen setIsSyncable()
bietet eine Möglichkeit, die Zeile zu deaktivieren und auszugrauen, damit der Benutzer sie nicht aktivieren kann. Sie können ein Konto synchronisieren und das andere nicht synchronisieren (dsabled).
7. Beachten Sie ContentResolver.notifyChange()
Eine heikle Sache. ContentResolver.notifyChange()
ist eine Funktion, die von verwendet wird ContentProvider
s, um Android zu benachrichtigen, dass die lokale Datenbank geändert wurde. Dies erfüllt zwei Funktionen, erstens bewirkt es, dass Cursor, die diesem Inhalts-URI folgen, aktualisiert werden und wiederum a erneut abfragen und ungültig machen und neu zeichnen ListView
etc… Es ist sehr magisch, die Datenbank ändert sich und Ihre ListView
nur automatisch aktualisiert. Genial. Wenn sich die Datenbank ändert, fordert Android auch außerhalb Ihres normalen Zeitplans eine Synchronisierung für Sie an, damit diese Änderungen so schnell wie möglich vom Gerät entfernt und mit dem Server synchronisiert werden. Auch toll.
Es gibt jedoch einen Randfall. Wenn Sie vom Server ziehen und ein Update in die schieben ContentProvider
wird es pflichtbewusst rufen notifyChange()
und Android wird sagen: “Oh, Datenbankänderungen, stell sie besser auf den Server!” (Doh!) Gut geschrieben ContentProviders
wird einige Tests durchführen, um festzustellen, ob die Änderungen vom Netzwerk oder vom Benutzer stammen, und den booleschen Wert festlegen syncToNetwork
Flag false, wenn dies der Fall ist, um diese verschwenderische Doppelsynchronisierung zu verhindern. Wenn Sie Daten in a einspeisen ContentProvider
müssen Sie herausfinden, wie Sie dies zum Laufen bringen — Andernfalls führen Sie am Ende immer zwei Synchronisierungen durch, wenn nur eine benötigt wird.
8. Fühlen Sie sich glücklich!
Sobald Sie alle diese XML-Metadaten an Ort und Stelle haben und die Synchronisierung aktiviert ist, weiß Android, wie es alles für Sie verbinden kann, und die Synchronisierung sollte funktionieren. An diesem Punkt werden viele schöne Dinge einfach einrasten und es wird sich wie Magie anfühlen. Genießen!
.
Mein Problem war, dass mein Unterbrechungspunkt im Sync-Adapter nicht erreicht wurde … Dann wurde mir klar, dass ich versuchte, einen Dienst zu debuggen … Hoffe, das hilft anderen wie mir.
– Dangalg
11. Mai 15 um 19:06 Uhr
Haltepunkte im Synchronisierungsadapterdienst werden NICHT ausgelöst. Das liegt daran, dass der Synchronisierungsadapterdienst in einem separaten Prozess ausgeführt wird. Darauf hat @danglang angedeutet. Siehe auch diese Frage: stackoverflow.com/questions/8559458/…
– Konstantin Schubert
4. Juli 15 um 21:04 Uhr
In meinem Fall entfernen
android:process=":sync"
vom Synchronisierungsdienst ließ den Debugger die Schnabelpunkte treffen. Der Synchronisierungsdienst selbst funktionierte vorher, weil ich Protokollnachrichten von sehen konnteonPerformSync
Methode im Namen eines anderen Prozesses.– Sergej
16. März 17 um 6:28 Uhr
Eine weitere Ursache ist das ausgeschaltete WLAN. Wenn Sie also versuchen, mit verspotteten Daten zu synchronisieren, überprüfen Sie Ihre Verbindung.
– Allan Veloso
13. Februar 19 um 20:51 Uhr