113 votes

Pourquoi ContentResolver.requestSync ne déclenche pas une synchronisation ?

Je suis en train de mettre en œuvre le Fournisseur de Contenu de Synchronisation de l'Adaptateur tel que discuté au Google IO - diapositive 26. Mon fournisseur de contenu est au travail, et ma synchro fonctionne quand je le provoquer de la Dev Outils de Synchronisation de Testeur de l'application, cependant quand je l'appelle ContentResolver.requestSync(compte, l'autorité, le bundle) de mon ContentProvider, mon sync n'est jamais déclenché.

ContentResolver.requestSync(
        account, 
        AUTHORITY, 
        new Bundle());

Edit -- ajoutée manifeste extrait de Mon manifeste xml contient:

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

--Edit

Mon syncadapter.xml associé avec mon service de synchronisation contient:

<?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"
/>

Pas sûr de ce que d'autres code serait utile. Le compte transmis à requestSync est de "myaccounttype" et l'AUTORITÉ transmise à l'appel à la hauteur de ma syc adaptateur xml.

Est ContentResolver.requestSync la bonne façon de demander une synchronisation? Il ressemble à la synchronisation outil de test se lie directement le service et les appels démarrer la synchronisation, mais qui semble comme il défait le but de s'intégrer dans l'architecture de synchronisation.

Si c'est la bonne façon de demander une synchronisation, alors pourquoi serait-sync testeur de travail, mais pas de mon appel à ContentResolver.requestSync? Il ya quelque chose que j'ai besoin de passer dans le bundle?

Je suis en train de tester dans l'émulateur sur les appareils exécutant 2.1 et 2.2.

Merci d'avance pour votre aide,
--Ben

282voto

jcwenger Points 6988

RequestSync() ne fonctionne que sur un (Compte, ContentAuthority} paire qui est connu du système. Votre application doit passer par un certain nombre d'étapes à dire Android que vous êtes capable de synchroniser un type spécifique de contenu à l'aide d'un type de compte. Il le fait dans le AndroidManifest.

1. Informer Android que votre application fournit des paquets de synchronisation

Tout d'abord, dans AndroidManifest.xml, vous devez déclarer que vous avez un Service de Synchronisation:

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

nom est le nom de votre classe de connecter jusqu'sync... je vais en parler à qui en une seconde.

exporté le rend visible à d'autres composants (nécessaire pour ContentResolver pouvez l'appeler comme ça).

Le filtre d'intention permet d'attraper une intention demandant de synchronisation. (C'est dans ce but vient de ContentResolver lorsque vous appelez votre RequestSync() de la fonction.)

2. Fournir des Android un service permet de trouver votre SyncAdapter

De sorte que la classe elle-même... en Voici un exemple:

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

Votre classe doit étendre le Service. Votre classe doit stocker une variable membre de type AbstractThreadedSyncAdapter, doit mettre en oeuvre public IBinder onBind(Intent), et doit retourner un SyncAdapterBinder lorsque cela s'appelle... Donc, comme vous pouvez le voir, c'est à peu près tout dans cette classe. La seule raison pour laquelle il est là pour fournir un Service, qui offre une interface standard pour Android pour la requête de votre classe, de votre SyncAdapter est lui-même.

3. Fournir un class SyncAdapter pour effectuer la synchronisation.

mySyncAdapter est là que la vraie logique de synchronisation lui-même est stocké. Elle est appelée (onPerformSync() est appelée) lorsqu'il est temps de synchronisation. Je suppose que vous avez déjà en place.

4. Établir une liaison entre un type de Compte et un Contenu Autorité

En regardant en arrière, de nouveau, à AndroidManifest, cette étrange méta-données de la balise dans notre service est l'élément clé qui établit la liaison entre un ContentAuthority et un compte. L'extérieur fait référence à un autre fichier xml (appelez ça comme vous voulez, quelque chose de pertinent à votre application.) Regardons sync_myapp.xml:

<?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" /> 

D'accord, alors à quoi ça sert? Il raconte Android que la synchronisation de l'adaptateur que nous avons définie (la classe qui a été appelé le nom de l'élément de l' <service> balise qui comprend l' <metadata> balise qui inclut ce fichier...) permettra de synchroniser les contacts à l'aide d'un com.google compte style.

Tous vos contentAuthority chaînes correspondent à tous, et en correspondance avec ce que vous synchronisez -- Ce doit être une chaîne de caractères que vous définissez, si vous êtes en train de créer votre propre base de données, ou vous devez utiliser certaines de périphérique existant chaînes si vous êtes à la synchronisation connu des types de données (comme les contacts ou le calendrier d'événements ou de ce que vous avez.) Le ci-dessus ("com.android.les contacts") se trouve être le ContentAuthority chaîne pour les contacts de type de données (surprise, surprise).

accountType doit également correspondre à l'un de ceux qu'on appelle les types de comptes qui sont déjà entrés, ou elle doit correspondre à celui que vous êtes en train de créer (Ce qui implique la création d'une sous-classe de AccountAuthenticator pour obtenir auth sur votre serveur... Un article qui vaut lui-même.) Encore une fois, "com.google" est la chaîne définie par l'identification... google.com style informations d'identification du compte (encore une fois, cela ne devrait pas être une surprise).

5. Activer la Synchronisation sur un Compte donné / ContentAuthority paire

Enfin, la synchronisation doit être activé. Vous pouvez le faire dans les Comptes Et Synchronisation de la page dans le panneau de contrôle en allant à votre application et en cochant la case à côté de votre application dans le compte correspondant. Alternativement, vous pouvez le faire dans une certaine configuration de code dans votre application:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

Pour la synchronisation, votre compte/autorité de la paire doit être activé pour la synchronisation (comme ci-dessus) et dans le monde de synchronisation drapeau sur le système doit être réglé, et l'appareil doit disposer d'une connectivité réseau.

Si votre compte/autorité de la synchronisation ou de la mondial de synchronisation sont désactivées, l'appel de RequestSync() n'ont un effet -- Il définit un indicateur que la synchronisation a été demandé, et sera réalisée dès que la synchronisation est activée.

Aussi, par mgv, paramètre ContentResolver.SYNC_EXTRAS_MANUAL de vrai dans le bundle extras de votre requestSync demandera android pour forcer une synchronisation, même si global sync est désactivé (être respectueux de votre utilisateur ici!)

Enfin, vous pouvez configurer un périodique planification de la synchronisation, de nouveau avec ContentResolver fonctions.

6. Examiner les implications de plusieurs comptes

Il est possible d'avoir plus d'un compte du même type (deux @gmail.com les comptes configurés sur un appareil ou deux facebook comptes, ou les deux comptes twitter, etc...), Vous devriez envisager l'application implications de ce que... Si vous avez deux comptes, vous ne voulez probablement pas à essayer de synchroniser les deux d'entre eux dans les mêmes tables de base de données. Peut-être vous avez besoin de spécifier qu'un seul peut être actif à la fois, et de vider les tables et resync si vous changez de compte. (par le biais d'une page de propriétés qui demande à ce que les comptes sont présents). Peut-être que vous créez une base de données différente pour chaque compte, peut-être des tables différentes, peut-être une clé de colonne de chaque table. Tous spécifiques à l'application et digne de réflexion. ContentResolver.setIsSyncable(Account account, String authority, int syncable) pourrait être d'intérêt ici. setSyncAutomatically() détermine si un compte/autorité de la paire est cochée ou décochée, alors que setIsSyncable() fournit un moyen de décocher et gris la ligne de sorte que l'utilisateur ne peut pas l'activer. Vous pouvez définir un compte Syncable et l'autre pas Syncable (dsabled).

7. Être conscient de ContentResolver.notifyChange()

Une chose la plus délicate. ContentResolver.notifyChange() est une fonction utilisée par ContentProviders de notifier Android que le local de la base de données a été modifié. Cela sert à deux fonctions, tout d'abord, il va provoquer les curseurs suivants qui contenturi de mettre à jour, et à son tour, d'actualisation et de l'invalider et de redessiner une ListView, etc... C'est vraiment magique, les modifications de base de données et votre ListView juste les mises à jour automatiquement. Génial. Aussi, lorsque les modifications de base de données, Android demande de Synchronisation pour vous, même en dehors de votre horaire normal, de sorte que ces modifications sont prises hors tension de l'appareil et synchroniser avec le serveur le plus rapidement possible. Aussi génial.

Il y a un cas limite. Si vous tirez à partir du serveur, et de pousser une mise à jour dans le ContentProvider, il consciencieusement appel notifyChange() et android va aller, "Oh, les modifications de base de données, de mieux les mettre sur le serveur!" (Doh!) Bien écrit ContentProviders aura quelques tests pour voir si les changements sont venus à partir du réseau ou de l'utilisateur, et permet de définir le type boolean syncToNetwork drapeau false si donc, pour éviter ce gaspillage double-sync. Si vous êtes à l'alimentation des données dans un ContentProvider, il vous appartient de comprendre comment obtenir ce travail, Sinon vous finirez toujours deux synchronise quand une seule est nécessaire.

8. Se sentir heureux!

Une fois que vous avez toutes ces métadonnées xml en place, et activé la synchronisation, Android saurez comment connecter le tout en place pour vous, et sync devrait commencer à travailler. À ce stade, beaucoup de choses qui sont nice suffit de cliquer en place et il va se sentir un peu comme de la magie. Profitez-en!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X