web-dev-qa-db-fra.com

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

J'essaie d'implémenter le modèle d'adaptateur de synchronisation de fournisseur de contenu, comme indiqué à la section Google IO - Diapositive 26. Mon fournisseur de contenu fonctionne et ma synchronisation fonctionne lorsque je le déclenche à partir de l'application Dev Tools Sync Tester. Toutefois, lorsque j'appelle ContentResolver.requestSync (compte, autorité, bundle) depuis ContentProvider, ma synchronisation n'est jamais déclenchée.

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

Éditer - extrait de manifeste ajouté 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>

--Modifier

Mon syncadapter.xml associé à 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"
/>

Vous ne savez pas quel autre code serait utile. Le compte transmis à requestSync est de "myaccounttype" et AUTHORITY transmis à l'appel correspond à mon adaptateur syc xml.

ContentResolver.requestSync est-il le bon moyen de demander une synchronisation? Il semble que l'outil de test de synchronisation se lie directement au service et que les appels démarrent sync, mais il semble qu'il soit contraire à l'objectif d'intégration avec l'architecture de synchronisation.

Si c'est le bon moyen de demander une synchronisation, alors pourquoi le testeur de synchronisation fonctionne-t-il, mais pas mon appel à ContentResolver.requestSync? Y at-il quelque chose que je dois passer dans le paquet?

Je teste dans l'émulateur sur des périphériques exécutant les versions 2.1 et 2.2.

111
Ben

L'appel de requestSync() ne fonctionnera que sur une paire {Account, ContentAuthority} connue du système. Votre application doit suivre un certain nombre d'étapes pour indiquer à Android) que vous êtes capable de synchroniser un type de contenu spécifique à l'aide d'un type de compte spécifique. Cela se fait dans le Manifeste Android.

1. Notify Android que votre package d'application assure la synchronisation

Tout d'abord, dans AndroidManifest.xml, vous devez déclarer que vous disposez d'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>

L'attribut name de la balise <service> Est le nom de votre classe à connecter à la synchronisation ... Je parlerai de cela dans une seconde.

Définir exporté à true le rend visible pour les autres composants (nécessaire pour que ContentResolver puisse l'appeler).

Le filtre d'intention lui permet d'attraper une intention demandant une synchronisation. (Ce Intent provient de ContentResolver lorsque vous appelez ContentResolver.requestSync() ou des méthodes de planification associées.)

La balise <meta-data> Sera discutée ci-dessous.

2. Fournissez Android un service utilisé pour trouver votre SyncAdapter

Donc, la classe elle-même ... 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 Service ou l'une de ses sous-classes, doit implémenter public IBinder onBind(Intent), et doit renvoyer un SyncAdapterBinder lorsque cela s'appelle ... Vous avez besoin d'une variable de type AbstractThreadedSyncAdapter. Comme vous pouvez le constater, c'est à peu près tout dans cette classe. La seule raison pour laquelle il existe est de fournir un service, qui offre une interface standard pour Android) pour interroger votre classe sur la nature de votre SyncAdapter lui-même.

3. Fournissez un class SyncAdapter Pour effectuer la synchronisation.

mySyncAdapter est l'endroit où la logique de synchronisation réelle elle-même est stockée. Sa méthode onPerformSync() est appelée lorsqu'il est temps de synchroniser. Je suppose que vous avez déjà cela en place.

4. Établissez une liaison entre un type de compte et une autorité de contenu

En regardant de nouveau AndroidManifest, cette étrange balise <meta-data> De notre service est l'élément clé qui établit la liaison entre un ContentAuthority et un compte. Il référence en externe un autre fichier xml (appelez-le comme bon vous semble, correspondant à votre application). Voyons 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" /> 

Ok, alors qu'est-ce que cela fait? Il indique à Android que l'adaptateur de synchronisation que nous avons défini (la classe appelée dans l'élément de nom de la balise <service> Qui inclut la balise <meta-data> qui fait référence à ce fichier ...) synchronisera les contacts à l'aide d'un compte de style com.google.

Toutes vos chaînes contentAuthority doivent toutes correspondre, et correspondre à ce que vous synchronisez - Ceci doit être une chaîne que vous définissez, si vous créez votre propre base de données, ou vous devez utiliser certaines chaînes de périphérique existantes si vous synchronisez de manière connue. types de données (comme des contacts ou des événements d'agenda ou ce que vous avez). La liste ci-dessus ("com.Android.contacts") est la chaîne ContentAuthority pour les données de type de contact (surprise, surprise.)

accountType doit également correspondre à l'un de ces types de compte connus déjà saisis ou à celui que vous créez (vous devez créer une sous-classe de AccountAuthenticator pour obtenir une autorisation sur votre serveur ... Cela vaut un article, lui-même.) Encore une fois, "com.google" est la chaîne définie identifiant ... les informations d'identification du compte de style google.com (encore une fois, cela ne devrait pas être une surprise.)

5. Activer la synchronisation sur une paire Account/ContentAuthority donnée

Enfin, la synchronisation doit être activée. Vous pouvez le faire dans la page Comptes et synchronisation du panneau de configuration en accédant à votre application et en activant la case à cocher en regard de votre application dans le compte correspondant. Alternativement, vous pouvez le faire dans un code d'installation de votre application:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

Pour que la synchronisation se produise, votre paire compte/autorité doit être activée pour se synchroniser (comme ci-dessus) et le drapeau global de synchronisation sur le système doit être défini, et = le périphérique doit avoir une connectivité réseau.

Si la synchronisation de votre compte/autorité ou la synchronisation globale est désactivée, l'appel de RequestSync () a effectivement un effet. Il indique si la synchronisation a été demandée et sera exécutée dès que la synchronisation est activée.

En outre, conformément à mgv , définir ContentResolver.SYNC_EXTRAS_MANUAL Sur true dans le groupe d’extras de votre requestSync demandera à Android de forcer la synchronisation même si la synchronisation globale est désactivée). (respectez votre utilisateur ici!)

Enfin, vous pouvez configurer une synchronisation périodique planifiée, à nouveau avec les fonctions ContentResolver.

6. Considérer les implications de plusieurs comptes

Il est possible d’avoir plusieurs comptes du même type (deux comptes @ gmail.com configurés sur un appareil ou deux comptes facebook, ou deux comptes Twitter, etc.). Vous devez tenir compte des implications de cela pour les applications. .. Si vous avez deux comptes, vous ne voulez probablement pas essayer de les synchroniser dans les mêmes tables de base de données. Peut-être devrez-vous spécifier qu'un seul peut être actif à la fois, vider les tables et les resynchroniser si vous changez de compte. (via une page de propriétés qui demande quels comptes sont présents). Peut-être créez-vous une base de données différente pour chaque compte, peut-être des tables différentes, peut-être une colonne clé dans chaque table. Toutes les applications spécifiques et dignes d'une certaine réflexion. ContentResolver.setIsSyncable(Account account, String authority, int syncable) pourrait être intéressant ici. setSyncAutomatically() contrôle si une paire compte/autorité est cochée ou décochée, alors que setIsSyncable() permet de décocher et grisez la ligne afin que l'utilisateur ne puisse pas l'allumer. Vous pouvez définir un compte Syncable et l'autre non Syncable (désactivé).

7. Soyez conscient de ContentResolver.notifyChange ()

Une chose délicate. ContentResolver.notifyChange() est une fonction utilisée par ContentProviders pour notifier Android que la base de données locale a été modifiée. Cela sert à deux fonctions. D'abord, les curseurs suivront ce contenu uri à mettre à jour, puis à nouveau interroger, invalider et redessiner un ListView, etc ... C’est très magique, la base de données change et votre ListView se met à jour automatiquement. la base de données change, Android demandera la synchronisation pour vous, même en dehors de votre calendrier normal, afin que ces modifications soient retirées de l'appareil et synchronisées sur le serveur aussi rapidement que possible. Aussi génial.

Il y a cependant un cas Edge. Si vous extrayez du serveur et insérez une mise à jour dans le ContentProvider, il appellera consciencieusement notifyChange() et Android ira, "Oh, modifications de la base de données Mieux vaut les mettre sur le serveur! "(Doh!) ContentProviders bien écrit va effectuer des tests pour voir si les modifications ont été apportées par le réseau ou par l'utilisateur, et définira le booléen syncToNetwork flag false si oui, pour éviter cette double synchronisation inutile. Si vous alimentez des données dans un ContentProvider, il vous incombe de déterminer comment le faire fonctionner - Sinon, vous finirez toujours par exécuter deux synchronisations quand un seul est nécessaire.

8. Se sentir heureux!

Une fois que vous avez mis toutes ces métadonnées XML en place et que la synchronisation est activée, Android saura comment tout connecter pour vous, et la synchronisation devrait commencer à fonctionner. À ce stade, beaucoup de choses qui Nice va simplement cliquer en place et vous vous sentirez beaucoup comme par magie.

279
jcwenger