Créer un adaptateur de synchronisation

Remarque:Nous vous recommandons d'utiliser WorkManager. est recommandée pour la plupart des cas d'utilisation du traitement en arrière-plan. Pour plus d'informations, veuillez consulter le guide de traitement en arrière-plan pour identifier la solution la mieux adaptée à vos besoins.

Le composant d'adaptateur de synchronisation de votre application encapsule le code pour les tâches transférées entre l'appareil et un serveur. En fonction de la planification et des déclencheurs que vous indiquez dans votre application, le framework d'adaptateur de synchronisation exécute le code du composant d'adaptateur de synchronisation. Pour ajouter un composant adaptateur de synchronisation à votre application, vous devez ajouter les éléments suivants:

Classe d'adaptateur de synchronisation.
Une classe qui encapsule votre code de transfert de données dans une interface compatible avec l'adaptateur de synchronisation. d'infrastructure.
Service lié.
Composant permettant au framework d'adaptateur de synchronisation d'exécuter le code de votre adaptateur de synchronisation. .
Fichier de métadonnées XML de l'adaptateur de synchronisation.
Fichier contenant des informations sur votre adaptateur de synchronisation. Le framework lit ce fichier découvrez comment charger et planifier votre transfert de données.
Déclarations dans le fichier manifeste de l'application
Fichier XML qui déclare le service lié et pointe vers les métadonnées spécifiques à l'adaptateur.

Cette leçon vous explique comment définir ces éléments.

Créer une classe d'adaptateur de synchronisation

Dans cette partie de la leçon, vous allez apprendre à créer la classe d'adaptateur de synchronisation qui encapsule l'objet code de transfert de données. La création de la classe inclut l'extension de la classe de base de l'adaptateur de synchronisation, la définition constructeurs pour la classe et implémenter la méthode permettant de définir le transfert de données tâches.

Étendre la classe de l'adaptateur de synchronisation de base

Pour créer le composant d'adaptateur de synchronisation, commencez par étendre AbstractThreadedSyncAdapter et l'écriture de ses constructeurs. Utilisez les constructeurs pour exécuter des tâches de configuration chaque fois que votre composant d'adaptateur de synchronisation est créé à partir de de la même manière que vous utilisez Activity.onCreate() pour configurer activité. Par exemple, si votre application utilise un fournisseur de contenu pour stocker des données, utilisez les constructeurs pour obtenir une instance ContentResolver. Puisqu'une deuxième forme du constructeur a été ajouté à la version 3.0 de la plate-forme Android pour prendre en charge parallelSyncs vous devez créer deux formes du constructeur pour maintenir la compatibilité.

Remarque:Le framework d'adaptateur de synchronisation est conçu pour fonctionner avec l'adaptateur de synchronisation. qui sont des instances singleton. Nous allons voir comment instancier le composant de l'adaptateur de synchronisation. dans la section Associez l'adaptateur de synchronisation au framework.

L'exemple suivant vous montre comment implémenter AbstractThreadedSyncAdapter et ses constructeurs:

Kotlin

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
class SyncAdapter @JvmOverloads constructor(
        context: Context,
        autoInitialize: Boolean,
        /**
         * Using a default argument along with @JvmOverloads
         * generates constructor for both method signatures to maintain compatibility
         * with Android 3.0 and later platform versions
         */
        allowParallelSyncs: Boolean = false,
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        val mContentResolver: ContentResolver = context.contentResolver
) : AbstractThreadedSyncAdapter(context, autoInitialize, allowParallelSyncs) {
    ...
}

Java

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
public class SyncAdapter extends AbstractThreadedSyncAdapter {
    ...
    // Global variables
    // Define a variable to contain a content resolver instance
    ContentResolver contentResolver;
    /**
     * Set up the sync adapter
     */
    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        contentResolver = context.getContentResolver();
    }
    ...
    /**
     * Set up the sync adapter. This form of the
     * constructor maintains compatibility with Android 3.0
     * and later platform versions
     */
    public SyncAdapter(
            Context context,
            boolean autoInitialize,
            boolean allowParallelSyncs) {
        super(context, autoInitialize, allowParallelSyncs);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        contentResolver = context.getContentResolver();
        ...
    }

Ajouter le code de transfert de données

Le composant d'adaptateur de synchronisation n'effectue pas automatiquement le transfert de données. Au lieu de cela, il encapsule votre code de transfert de données, de sorte que le framework de l'adaptateur de synchronisation puisse exécuter la transfert de données en arrière-plan, sans intervention de votre application. Lorsque le framework est prêt pour synchroniser les données de votre application, l'implémentation de la méthode onPerformSync()

Pour faciliter le transfert des données du code de votre application principale vers le composant de l'adaptateur de synchronisation, le framework de l'adaptateur de synchronisation appelle onPerformSync() avec le les arguments suivants:

Compte
Un objet Account associé à l'événement qui a déclenché l'adaptateur de synchronisation. Si votre serveur n'utilise pas de comptes, vous n'avez pas besoin d'utiliser la dans cet objet.
Bonus
Bundle contenant les indicateurs envoyés par l'événement ayant déclenché la synchronisation. adaptateur secteur.
Autorité
L'autorité d'un fournisseur de contenu dans le système. Votre application doit avoir accès à ce fournisseur. En général, l'autorité correspond à un fournisseur de contenu dans votre propre application.
Client du fournisseur de contenu
ContentProviderClient pour le fournisseur de contenu vers lequel l'élément l'argument "authority". Un ContentProviderClient est un fichier public léger à un fournisseur de contenu. Il offre les mêmes fonctionnalités de base qu'un ContentResolver Si vous utilisez un fournisseur de contenu pour stocker des données pour votre application, vous pouvez vous connecter au fournisseur avec cet objet. Sinon, vous pouvez ignorer
Résultat de la synchronisation
Un objet SyncResult que vous utilisez pour envoyer des informations à la synchronisation un framework d'adaptateurs.

L'extrait de code suivant montre la structure globale onPerformSync():

Kotlin

/*
 * Specify the code you want to run in the sync adapter. The entire
 * sync adapter runs in a background thread, so you don't have to set
 * up your own background processing.
 */
override fun onPerformSync(
        account: Account,
        extras: Bundle,
        authority: String,
        provider: ContentProviderClient,
        syncResult: SyncResult
) {
    /*
     * Put the data transfer code here.
     */
}

Java

/*
 * Specify the code you want to run in the sync adapter. The entire
 * sync adapter runs in a background thread, so you don't have to set
 * up your own background processing.
 */
@Override
public void onPerformSync(
        Account account,
        Bundle extras,
        String authority,
        ContentProviderClient provider,
        SyncResult syncResult) {
    /*
     * Put the data transfer code here.
     */
}

Bien que l'implémentation réelle onPerformSync() est spécifique à les exigences de synchronisation des données et les protocoles de connexion au serveur de votre application, tâches générales que votre implémentation doit effectuer:

Connexion à un serveur
Bien que vous puissiez partir du principe que le réseau est disponible au début du transfert de données, l'infrastructure d'adaptateur de synchronisation ne se connecte pas automatiquement à un serveur.
Télécharger et importer des données
Un adaptateur de synchronisation n'automatise pas les tâches de transfert de données. Si vous voulez télécharger d'un serveur et les stocker chez un fournisseur de contenu, vous devez fournir le code demande les données, les télécharge et les insère dans le fournisseur. De même, si vous souhaitez envoyer des données à un serveur, vous devez les lire à partir d'un fichier, d'une base de données ou d'un fournisseur, la demande d'importation nécessaire. Vous devez également gérer les erreurs réseau qui se produisent le transfert de données est en cours.
Gérer les conflits de données ou déterminer l'actualité des données
Un adaptateur de synchronisation ne gère pas automatiquement les conflits entre les données sur le serveur et celles sur l'appareil. De plus, il ne détecte pas automatiquement si les données présentes sur le serveur sont plus récentes que des données sur l'appareil, ou inversement. Vous devez fournir vos propres algorithmes gérer cette situation.
Effectuer un nettoyage.
Fermez toujours les connexions à un serveur et nettoyez les fichiers temporaires et les caches à la fin de votre transfert de données.

Remarque:Le framework d'adaptateur de synchronisation s'exécute onPerformSync() sur un ce qui vous évite d'avoir à configurer votre propre traitement en arrière-plan.

En plus de vos tâches de synchronisation, essayez de combiner vos tâches habituelles des tâches liées au réseau et les ajouter onPerformSync() En concentrant toutes vos tâches réseau avec cette méthode, vous préservez l'autonomie de la batterie nécessaires pour démarrer et arrêter les interfaces réseau. Pour en savoir plus sur la façon de rendre l'accès au réseau plus consultez la formation Transférer des données sans vider la batterie, qui décrit plusieurs méthodes d'accès réseau que vous pouvez inclure dans votre code de transfert de données.

Lier l'adaptateur de synchronisation au framework

Votre code de transfert de données est à présent encapsulé dans un composant d'adaptateur de synchronisation. pour permettre au framework d'accéder à votre code. Pour ce faire, vous devez créer une limite Service qui transmet un objet de liaison Android spécial à partir de l'adaptateur de synchronisation dans le framework. Avec cet objet de liaison, le framework peut appeler la méthode onPerformSync() et de lui transmettre des données.

Instanciez votre composant d'adaptateur de synchronisation en tant que singleton dans le Méthode onCreate() du service. En instanciant composant dans onCreate(), vous différez jusqu'au démarrage du service, ce qui se produit lorsque le framework tente d'exécuter pour la première fois votre transfert de données. Vous devez instancier le composant de façon thread-safe, au cas où la synchronisation le framework d'adaptateur place en file d'attente plusieurs exécutions de votre adaptateur de synchronisation en réponse à des déclencheurs ou la planification.

Par exemple, l'extrait de code suivant vous montre comment créer une classe qui implémente la classe lié Service, instancie le composant de l'adaptateur de synchronisation et récupère Objet de liaison Android:

Kotlin

package com.example.android.syncadapter
/**
 * Define a Service that returns an [android.os.IBinder] for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
class SyncService : Service() {
    /*
     * Instantiate the sync adapter object.
     */
    override fun onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized(sSyncAdapterLock) {
            sSyncAdapter = sSyncAdapter ?: SyncAdapter(applicationContext, true)
        }
    }

    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    override fun onBind(intent: Intent): IBinder {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         *
         * We should never be in a position where this is called before
         * onCreate() so the exception should never be thrown
         */
        return sSyncAdapter?.syncAdapterBinder ?: throw IllegalStateException()
    }

    companion object {
        // Storage for an instance of the sync adapter
        private var sSyncAdapter: SyncAdapter? = null
        // Object to use as a thread-safe lock
        private val sSyncAdapterLock = Any()
    }
}

Java

package com.example.android.syncadapter;
/**
 * Define a Service that returns an <code><a href="/reference/android/os/IBinder.html">IBinder</a></code> for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
public class SyncService extends Service {
    // Storage for an instance of the sync adapter
    private static SyncAdapter sSyncAdapter = null;
    // Object to use as a thread-safe lock
    private static final Object sSyncAdapterLock = new Object();
    /*
     * Instantiate the sync adapter object.
     */
    @Override
    public void onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }
    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    @Override
    public IBinder onBind(Intent intent) {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         */
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

Remarque:Pour voir un exemple plus détaillé d'un service lié à un adaptateur de synchronisation, consultez l'application exemple.

Ajouter le compte requis par le framework

Le framework d'adaptateurs de synchronisation nécessite que chaque adaptateur de synchronisation soit associé à un type de compte. Vous avez déclaré la valeur du type de compte dans la section Ajoutez le fichier de métadonnées Authenticator. Vous devez maintenant configurer ce type de compte Système Android. Pour configurer le type de compte, ajoutez un compte réservé qui utilise le type de compte en appelant addAccountExplicitly().

Le meilleur endroit pour appeler la méthode est dans la onCreate() de la méthode l'activité d'ouverture. L'extrait de code suivant vous montre comment procéder:

Kotlin

...
// Constants
// The authority for the sync adapter's content provider
const val AUTHORITY = "com.example.android.datasync.provider"
// An account type, in the form of a domain name
const val ACCOUNT_TYPE = "example.com"
// The account name
const val ACCOUNT = "placeholderaccount"
...
class MainActivity : FragmentActivity() {

    // Instance fields
    private lateinit var mAccount: Account
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
       ...
        // Create the placeholder account
        mAccount = createSyncAccount()
       ...
    }
    ...
    /**
     * Create a new placeholder account for the sync adapter
     */
    private fun createSyncAccount(): Account {
        val accountManager = getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
        return Account(ACCOUNT, ACCOUNT_TYPE).also { newAccount ->
            /*
             * Add the account and account type, no password or user data
             * If successful, return the Account object, otherwise report an error.
             */
            if (accountManager.addAccountExplicitly(newAccount, null, null)) {
                /*
                 * If you don't set android:syncable="true" in
                 * in your <provider> element in the manifest,
                 * then call context.setIsSyncable(account, AUTHORITY, 1)
                 * here.
                 */
            } else {
                /*
                 * The account exists or some other error occurred. Log this, report it,
                 * or handle it internally.
                 */
            }
        }
    }
    ...
}

Java

public class MainActivity extends FragmentActivity {
    ...
    ...
    // Constants
    // The authority for the sync adapter's content provider
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // An account type, in the form of a domain name
    public static final String ACCOUNT_TYPE = "example.com";
    // The account name
    public static final String ACCOUNT = "placeholderaccount";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Create the placeholder account
        mAccount = CreateSyncAccount(this);
        ...
    }
    ...
    /**
     * Create a new placeholder account for the sync adapter
     *
     * @param context The application context
     */
    public static Account CreateSyncAccount(Context context) {
        // Create the account type and default account
        Account newAccount = new Account(
                ACCOUNT, ACCOUNT_TYPE);
        // Get an instance of the Android account manager
        AccountManager accountManager =
                (AccountManager) context.getSystemService(
                        ACCOUNT_SERVICE);
        /*
         * Add the account and account type, no password or user data
         * If successful, return the Account object, otherwise report an error.
         */
        if (accountManager.addAccountExplicitly(newAccount, null, null)) {
            /*
             * If you don't set android:syncable="true" in
             * in your <provider> element in the manifest,
             * then call context.setIsSyncable(account, AUTHORITY, 1)
             * here.
             */
        } else {
            /*
             * The account exists or some other error occurred. Log this, report it,
             * or handle it internally.
             */
        }
    }
    ...
}

Ajouter le fichier de métadonnées de l'adaptateur de synchronisation

Pour connecter votre composant d'adaptateur de synchronisation au framework, vous devez fournir le framework avec des métadonnées qui décrivent le composant et fournissent des options supplémentaires. Les métadonnées spécifient le type de compte que vous avez créé pour votre adaptateur de synchronisation, déclare une autorité de fournisseur de contenu associé à votre application, contrôle une partie de l'interface utilisateur du système liée aux adaptateurs de synchronisation, et déclare d'autres options liées à la synchronisation. Déclarez ces métadonnées dans un fichier XML spécial stocké dans le répertoire /res/xml/ dans votre projet d'application. Vous pouvez donner n’importe quel nom au fichier, bien qu'elle porte généralement le nom syncadapter.xml.

Ce fichier XML contient un seul élément XML <sync-adapter> dont le les attributs suivants:

android:contentAuthority
Autorité d'URI de votre fournisseur de contenu. Si vous avez créé un fournisseur de contenu bouchon pour votre application lors de la leçon précédente Créer un fournisseur de contenu stub, utilisez la valeur spécifiée pour le paramètre attribut android:authorities dans l'élément <provider> que vous avez ajouté au fichier manifeste de votre application. Cet attribut est décrits plus en détail dans la section Déclarez le fournisseur dans le fichier manifeste.
Si vous transférez des données d'un fournisseur de contenu vers un serveur à l'aide de votre adaptateur de synchronisation, doit être identique à l'autorité d'URI de contenu que vous utilisez pour ces données. Cette valeur est également l'une des autorités que vous spécifiez dans le android:authorities de l'élément <provider> qui déclare votre fournisseur dans le fichier manifeste de votre application.
android:accountType
Type de compte requis par le framework d'adaptateur de synchronisation. La valeur doit être identique comme valeur de type de compte que vous avez fournie lors de la création du fichier de métadonnées de l'authentificateur, décrit dans la section Ajouter le fichier de métadonnées Authenticator. Il s'agit également de la valeur que vous avez spécifiée pour le paramètre constante ACCOUNT_TYPE dans l'extrait de code de la section Ajoutez le compte requis par le cadre.
Attributs des paramètres
android:userVisible
Définit la visibilité du type de compte de l'adaptateur de synchronisation. Par défaut, l'icône de compte et le libellé associés au type de compte sont visibles dans Dans la section Comptes de l'application Paramètres du système, effectuez la synchronisation l'adaptateur invisible, sauf si vous avez un type de compte ou un domaine facilement associé avec votre application. Si vous rendez votre type de compte invisible, vous pouvez toujours autoriser les utilisateurs à contrôler votre adaptateur de synchronisation avec une interface utilisateur dans l'une des activités de votre application.
android:supportsUploading
Vous permet d'importer des données dans le cloud. Définissez cette valeur sur false si votre application uniquement télécharge des données.
android:allowParallelSyncs
Permet à plusieurs instances du composant d'adaptateur de synchronisation de s'exécuter simultanément. Utilisez cette option si votre application prend en charge plusieurs comptes utilisateur et que vous souhaitez autoriser plusieurs de transférer des données en parallèle. Cet indicateur n'a aucun effet si vous n'exécutez jamais plusieurs transferts de données.
android:isAlwaysSyncable
Indique au framework d'adaptateur de synchronisation qu'il peut exécuter votre adaptateur de synchronisation à tout moment que vous avez spécifiée. Si vous souhaitez programmer le moment où votre synchronisation l'adaptateur peut s'exécuter, définissez cet indicateur sur false, puis appelez requestSync() pour exécuter adaptateur de synchronisation. Pour en savoir plus sur l'exécution d'un adaptateur de synchronisation, consultez la leçon Exécuter un adaptateur de synchronisation

L'exemple suivant présente le code XML d'un adaptateur de synchronisation qui utilise un seul compte à espace réservé et ne fait que les téléchargements.

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:contentAuthority="com.example.android.datasync.provider"
        android:accountType="com.android.example.datasync"
        android:userVisible="false"
        android:supportsUploading="false"
        android:allowParallelSyncs="false"
        android:isAlwaysSyncable="true"/>

Déclarer l'adaptateur de synchronisation dans le fichier manifeste

Une fois que vous avez ajouté le composant d'adaptateur de synchronisation à votre application, vous devez demander des autorisations lié à l'utilisation du composant, et vous devez déclarer le Service lié que vous avez ajoutés.

Comme le composant d'adaptateur de synchronisation exécute du code qui transfère les données entre le réseau et le appareil, vous devez demander l'autorisation d'accéder à Internet. De plus, votre application doit pour demander l'autorisation de lire et d'écrire les paramètres de l'adaptateur de synchronisation afin que vous puissiez contrôler la synchronisation l'adaptateur de manière programmatique à partir d'autres composants de votre application. Vous devez également demander un autorisation spéciale permettant à votre appli d'utiliser le composant Authenticator que vous avez créé reportez-vous à la leçon Créer un authentificateur stub.

Pour demander ces autorisations, ajoutez les éléments suivants au fichier manifeste de votre application en tant qu'éléments enfants de <manifest>:

android.permission.INTERNET
Autorise le code de l'adaptateur de synchronisation à accéder à Internet pour télécharger ou importer des données de l'appareil à un serveur. Vous n'avez pas besoin de l'ajouter à nouveau l'ayant déjà demandé.
android.permission.READ_SYNC_SETTINGS
Permet à votre application de lire les paramètres actuels de l'adaptateur de synchronisation. Par exemple, vous avez besoin que l'autorisation pour appeler getIsSyncable().
android.permission.WRITE_SYNC_SETTINGS
Permet à votre application de contrôler les paramètres de l'adaptateur de synchronisation. Vous avez besoin de cette autorisation pour : définir des exécutions de l'adaptateur de synchronisation périodique à l'aide de addPeriodicSync(). Cette autorisation n'est pas requise pour appeler requestSync() Pour en savoir plus sur exécutant l'adaptateur de synchronisation, consultez la section Exécuter un adaptateur de synchronisation.

L'extrait de code suivant montre comment ajouter ces autorisations:

<manifest>
...
    <uses-permission
            android:name="android.permission.INTERNET"/>
    <uses-permission
            android:name="android.permission.READ_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.WRITE_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
...
</manifest>

Enfin, pour déclarer la Service liée que le framework utilise pour interagissent avec votre adaptateur de synchronisation, ajoutez le code XML suivant au fichier manifeste de votre application en tant qu'élément enfant. sur <application>:

        <service
                android:name="com.example.android.datasync.SyncService"
                android:exported="false"
                android:process=":sync">
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
            </intent-filter>
            <meta-data android:name="android.content.SyncAdapter"
                    android:resource="@xml/syncadapter" />
        </service>

La <intent-filter> configure un filtre déclenché par l'action d'intent. android.content.SyncAdapter, envoyé par le système pour exécuter l'adaptateur de synchronisation. Lorsque le filtre est déclenché, le système démarre le service lié que vous avez créé, qui, dans cet exemple, SyncService L'attribut android:exported="false" n'autorise que votre application et le système à accéder Service L'attribut android:process=":sync" indique au système d'exécuter Service dans un processus partagé global nommé sync Si vous avez plusieurs adaptateurs de synchronisation dans votre application, ils peuvent partager ce processus, ce qui réduit les frais généraux.

La <meta-data> fournit le nom du fichier XML de métadonnées de l'adaptateur de synchronisation que vous avez créé précédemment. La android:name indique que ces métadonnées sont destinées au framework d'adaptateur de synchronisation. La android:resource spécifie le nom du fichier de métadonnées.

Tous les composants de votre adaptateur de synchronisation sont à présent installés. La leçon suivante vous explique comment au framework d'adaptateur de synchronisation d'exécuter l'adaptateur, soit en réponse à un événement, soit lors un calendrier régulier.