Exécuter 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.

Dans les leçons précédentes de ce cours, vous avez appris à créer un composant d'adaptateur de synchronisation encapsule le code de transfert de données, et explique comment ajouter d'autres composants qui vous permettent de Branchez l'adaptateur de synchronisation au système. Vous avez maintenant tout ce dont vous avez besoin pour installer une application qui inclut un adaptateur de synchronisation, mais aucun des codes que vous avez vus n'exécute réellement cet adaptateur.

Essayez d'exécuter votre adaptateur de synchronisation selon une programmation ou comme résultat indirect d'une . Par exemple, vous souhaitez peut-être que votre adaptateur de synchronisation s'exécute de façon régulière, soit après une pour une certaine période ou une heure précise de la journée. Vous pouvez également exécuter votre synchronisation adaptateur en cas de modification des données stockées sur l'appareil. Évitez d'exécuter votre l'adaptateur de synchronisation à la suite d'une action de l'utilisateur, car vous n'obtiendrez pas de la capacité de planification du framework d'adaptateur de synchronisation. Par exemple, vous devez éviter fournir un bouton d'actualisation dans votre interface utilisateur.

Vous disposez des options suivantes pour exécuter votre adaptateur de synchronisation:

Lorsque les données du serveur changent
Exécutez l'adaptateur de synchronisation en réponse à un message provenant d'un serveur, indiquant que l'état du serveur les données ont changé. Cette option vous permet d'actualiser les données du serveur vers l'appareil sans dégrader les performances ni de gaspiller l'autonomie de la batterie en interrogeant le serveur.
Lorsque les données de l'appareil changent
Exécutez un adaptateur de synchronisation lorsque les données changent sur l'appareil. Cette option vous permet d'envoyer des données modifiées entre l'appareil et un serveur, ce qui est particulièrement utile si vous devez vous assurer que le serveur dispose toujours des données les plus récentes de l'appareil. Cette option est simple à utiliser si vous stockez des données chez votre fournisseur de contenu. Si vous utilisez un bouchon fournisseur de contenu, détecter les modifications apportées aux données peut être plus difficile.
À intervalles réguliers
Exécutez un adaptateur de synchronisation à l'expiration de l'intervalle de votre choix ou à une certaine fréquence chaque jour.
À la demande
Exécutez l'adaptateur de synchronisation en réponse à une action de l'utilisateur. Toutefois, pour offrir aux utilisateurs vous devez privilégier l'une des options les plus automatisées. En utilisant les options d'automatisation, vous préservez la batterie et les ressources réseau.

Le reste de cette leçon décrit chacune de ces options plus en détail.

Exécuter l'adaptateur de synchronisation lorsque les données du serveur changent

Si votre application transfère des données à partir d'un serveur et que les données du serveur changent fréquemment, vous pouvez utiliser un adaptateur de synchronisation pour effectuer des téléchargements en réponse aux modifications de données. Pour exécuter l'adaptateur de synchronisation, le serveur envoie un message spécial à un BroadcastReceiver dans votre application. En réponse à ce message, appelez ContentResolver.requestSync() pour signaler au framework de l'adaptateur de synchronisation qu'il doit exécuter votre adaptateur de synchronisation.

Google Cloud Messaging (GCM) fournit à la fois le serveur et les composants d'appareil dont vous avez besoin pour faire fonctionner ce système de messagerie. Utilisation de GCM pour déclencher les transferts est plus fiable et plus efficace que la scrutation de l'état des serveurs. Pendant le sondage nécessite un élément Service toujours actif, GCM utilise un élément BroadcastReceiver, qui est activé lorsqu'un message arrive. Pendant le sondage à intervalles réguliers utilise la batterie, même si aucune mise à jour n'est disponible, GCM n'envoie messages en cas de besoin.

Remarque:Si vous utilisez GCM pour déclencher votre adaptateur de synchronisation via une diffusion à tous appareils sur lesquels votre application est installée, n'oubliez pas qu'ils reçoivent votre message sur à peu près en même temps. Dans ce cas, plusieurs instances de votre adaptateur de synchronisation peuvent s'exécuter en même temps, entraînant une surcharge du serveur et du réseau. Pour éviter cette situation lors d'une diffusion, sur tous les appareils, nous vous recommandons de reporter le démarrage de l'adaptateur de synchronisation. propre à chaque appareil.

L'extrait de code suivant vous montre comment exécuter requestSync() en réponse à une message GCM entrant:

Kotlin

...
// Constants
// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Account type
const val ACCOUNT_TYPE = "com.example.android.datasync"
// Account
const val ACCOUNT = "default_account"
// Incoming Intent key for extended data
const val KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST"
...
class GcmBroadcastReceiver : BroadcastReceiver() {
    ...
    override fun onReceive(context: Context, intent: Intent) {
        // Get a GCM object instance
        val gcm: GoogleCloudMessaging = GoogleCloudMessaging.getInstance(context)
        // Get the type of GCM message
        val messageType: String? = gcm.getMessageType(intent)
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE == messageType
            && intent.getBooleanExtra(KEY_SYNC_REQUEST, false)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null)
            ...
        }
        ...
    }
    ...
}

Java

public class GcmBroadcastReceiver extends BroadcastReceiver {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Incoming Intent key for extended data
    public static final String KEY_SYNC_REQUEST =
            "com.example.android.datasync.KEY_SYNC_REQUEST";
    ...
    @Override
    public void onReceive(Context context, Intent intent) {
        // Get a GCM object instance
        GoogleCloudMessaging gcm =
                GoogleCloudMessaging.getInstance(context);
        // Get the type of GCM message
        String messageType = gcm.getMessageType(intent);
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
            &&
            intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null);
            ...
        }
        ...
    }
    ...
}

Exécuter l'adaptateur de synchronisation lorsque les données du fournisseur de contenu sont modifiées

Si votre application collecte des données auprès d'un fournisseur de contenu et que vous souhaitez mettre à jour le serveur vous mettez à jour le fournisseur, vous pouvez configurer votre application pour qu'elle exécute votre adaptateur de synchronisation automatiquement. À faire vous enregistrez un observateur pour le fournisseur de contenu. Lorsque des données de votre fournisseur de contenu le framework du fournisseur de contenu appelle l'observateur. Dans l'observateur, appelez requestSync() pour indiquer au framework de s'exécuter votre adaptateur de synchronisation.

Remarque:Si vous utilisez un fournisseur de contenu bouchon, vous n'avez aucune donnée dans le fournisseur de contenu, et onChange() jamais appelé. Dans ce cas, vous devez fournir votre propre mécanisme pour détecter les modifications de les données de l'appareil. Ce mécanisme permet également d'appeler requestSync() lorsque les données changent.

Pour créer un observateur pour votre fournisseur de contenu, étendez la classe ContentObserver et implémenter les deux formes de son onChange(). Dans onChange(), appeler requestSync() pour démarrer l'adaptateur de synchronisation.

Pour enregistrer l'observateur, transmettez-le en tant qu'argument dans un appel à registerContentObserver() Dans cet appel, vous devez également transmettre un URI de contenu pour les données que vous souhaitez surveiller. Le contenu Provider Framework compare cet URI de montre aux URI de contenu transmis en tant qu'arguments à ContentResolver qui modifient votre fournisseur, telles que ContentResolver.insert() Si une correspondance est trouvée, votre implémentation de ContentObserver.onChange() est appelé.

L'extrait de code suivant vous montre comment définir un ContentObserver qui appelle requestSync() lorsqu'une table modifications:

Kotlin

// Constants
// Content provider scheme
const val SCHEME = "content://"
// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Path for the content provider table
const val TABLE_PATH = "data_table"
...
class MainActivity : FragmentActivity() {
    ...
    // A content URI for the content provider's data table
    private lateinit var uri: Uri
    // A content resolver for accessing the provider
    private lateinit var mResolver: ContentResolver
    ...
    inner class TableObserver(...) : ContentObserver(...) {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        override fun onChange(selfChange: Boolean) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null)
        }

        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        override fun onChange(selfChange: Boolean, changeUri: Uri?) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
             */
            ContentResolver.requestSync(account, AUTHORITY, null)
        }
        ...
    }
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        // Get the content resolver object for your app
        mResolver = contentResolver
        // Construct a URI that points to the content provider data table
        uri = Uri.Builder()
                .scheme(SCHEME)
                .authority(AUTHORITY)
                .path(TABLE_PATH)
                .build()
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        val observer = TableObserver(false)
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(uri, true, observer)
        ...
    }
    ...
}

Java

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider scheme
    public static final String SCHEME = "content://";
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Path for the content provider table
    public static final String TABLE_PATH = "data_table";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content URI for the content provider's data table
    Uri uri;
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    public class TableObserver extends ContentObserver {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        @Override
        public void onChange(boolean selfChange) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null);
        }
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        @Override
        public void onChange(boolean selfChange, Uri changeUri) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null);
        }
        ...
    }
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver object for your app
        mResolver = getContentResolver();
        // Construct a URI that points to the content provider data table
        uri = new Uri.Builder()
                  .scheme(SCHEME)
                  .authority(AUTHORITY)
                  .path(TABLE_PATH)
                  .build();
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        TableObserver observer = new TableObserver(false);
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(uri, true, observer);
        ...
    }
    ...
}

Exécuter régulièrement l'adaptateur de synchronisation

Vous pouvez exécuter régulièrement votre adaptateur de synchronisation en définissant un délai d'attente entre les exécutions. ou en l'exécutant à certains moments de la journée, ou les deux. Exécuter l'adaptateur de synchronisation permet régulièrement de correspondre à peu près à l'intervalle de mise à jour de votre serveur.

De même, vous pouvez importer des données depuis l'appareil lorsque votre serveur est relativement inactif : planifier l'exécution de votre adaptateur de synchronisation la nuit. La plupart des utilisateurs laissent leur appareil allumé et branché la nuit. C'est donc généralement disponible à cette heure. De plus, l'appareil n'exécute aucune autre tâche dans en même temps que votre adaptateur de synchronisation. Toutefois, si vous adoptez cette approche, chaque appareil déclenche un transfert de données à un moment légèrement différent. Si tous les appareils exécutent votre l'adaptateur secteur en parallèle, vous risquez de surcharger les données de votre serveur et de votre opérateur mobile. réseaux sociaux.

En général, les exécutions périodiques sont utiles si vos utilisateurs n'ont pas besoin de mises à jour instantanées, mais qu'ils s'attendent à des mises à jour régulières. Les exécutions périodiques sont également utiles si vous voulez équilibrer la disponibilité des données actualisées grâce à l'efficacité d'un adaptateur de synchronisation plus petit qui n'utilise pas trop l'appareil. ressources.

Pour faire fonctionner l'adaptateur de synchronisation à intervalles réguliers, appelez addPeriodicSync() Cela planifie votre l'adaptateur de synchronisation doit s'exécuter après un certain temps. Étant donné que le framework d'adaptateur de synchronisation prend en compte d'autres exécutions de l'adaptateur de synchronisation et tente d'optimiser l'efficacité de la batterie, le temps écoulé peut varier de quelques secondes. En outre, le framework n'exécutera pas votre adaptateur de synchronisation si le réseau non disponible.

Notez que addPeriodicSync() n'a pas exécuter l'adaptateur de synchronisation à un moment précis de la journée. Pour que l'adaptateur de synchronisation fonctionne à environ chaque jour à la même heure, utilisez une alarme récurrente comme déclencheur. Les alarmes répétées sont décrites plus en détail consultez la documentation de référence sur AlarmManager. Si vous utilisez les la méthode setInexactRepeating() pour définir de l'heure de début avec des variations, vous devez toujours randomiser l'heure de assurez-vous que l'adaptateur de synchronisation fonctionne sur différents appareils.

La méthode addPeriodicSync() ne permet pas désactiver setSyncAutomatically(), Vous pouvez donc exécuter plusieurs synchronisations dans un laps de temps relativement court. Par ailleurs, seuls quelques les indicateurs de contrôle de l'adaptateur de synchronisation sont autorisés dans un appel addPeriodicSync(); les indicateurs ne sont pas autorisés sont décrits dans la documentation de référence addPeriodicSync()

L'extrait de code suivant montre comment planifier des exécutions périodiques de l'adaptateur de synchronisation:

Kotlin

// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Account
const val ACCOUNT = "default_account"
// Sync interval constants
const val SECONDS_PER_MINUTE = 60L
const val SYNC_INTERVAL_IN_MINUTES = 60L
const val SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE
...
class MainActivity : FragmentActivity() {
    ...
    // A content resolver for accessing the provider
    private lateinit var mResolver: ContentResolver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        // Get the content resolver for your app
        mResolver = contentResolver
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                mAccount,
                AUTHORITY,
                Bundle.EMPTY,
                SYNC_INTERVAL)
        ...
    }
    ...
}

Java

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Sync interval constants
    public static final long SECONDS_PER_MINUTE = 60L;
    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
    public static final long SYNC_INTERVAL =
            SYNC_INTERVAL_IN_MINUTES *
            SECONDS_PER_MINUTE;
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                mAccount,
                AUTHORITY,
                Bundle.EMPTY,
                SYNC_INTERVAL);
        ...
    }
    ...
}

Exécuter l'adaptateur de synchronisation à la demande

La stratégie la moins conseillée est d'exécuter votre adaptateur de synchronisation en réponse à la demande d'un utilisateur. pour exécuter un adaptateur de synchronisation. Le framework est spécialement conçu pour préserver l'autonomie de la batterie. lorsqu'il exécute des adaptateurs de synchronisation selon un calendrier. Options qui exécutent une synchronisation en réponse aux données utilisent efficacement la batterie, car celle-ci sert à fournir de nouvelles données.

En revanche, si vous permettez aux utilisateurs d'exécuter une synchronisation à la demande, la synchronisation s'exécute seule, ce qui est une utilisation inefficace des ressources réseau et énergétiques. De plus, la synchronisation à la demande conduit les utilisateurs demander une synchronisation même si rien n'indique que les données ont changé et exécuter une synchronisation qui n'actualise pas les données est une utilisation inefficace de la batterie. En général, votre application doit : utiliser d'autres signaux pour déclencher une synchronisation ou les planifier à intervalles réguliers, sans intervention de l'utilisateur.

Toutefois, si vous souhaitez toujours exécuter l'adaptateur de synchronisation à la demande, définissez les indicateurs correspondants pour une manuellement l'adaptateur de synchronisation, puis appelez ContentResolver.requestSync()

Exécuter des transferts à la demande avec les options suivantes:

SYNC_EXTRAS_MANUAL
Force une synchronisation manuelle. Le framework d'adaptateur de synchronisation ignore les paramètres existants, comme l'option définie par setSyncAutomatically().
SYNC_EXTRAS_EXPEDITED
Force le démarrage immédiat de la synchronisation. Si vous ne définissez pas cette valeur, le système peut attendre plusieurs secondes avant d'exécuter la demande de synchronisation, car elle essaie d'optimiser l'utilisation de la batterie en planifier de nombreuses requêtes en peu de temps.

L'extrait de code suivant vous montre comment appeler requestSync() en réponse à un bouton cliquez sur:

Kotlin

// Constants
// Content provider authority
val AUTHORITY = "com.example.android.datasync.provider"
// Account type
val ACCOUNT_TYPE = "com.example.android.datasync"
// Account
val ACCOUNT = "default_account"
...
class MainActivity : FragmentActivity() {
    ...
    // Instance fields
    private lateinit var mAccount: Account
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        /*
         * Create the placeholder account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = createSyncAccount()
        ...
    }

    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    fun onRefreshButtonClick(v: View) {
        // Pass the settings flags by inserting them in a bundle
        val settingsBundle = Bundle().apply {
            putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true)
            putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true)
        }
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle)
    }

Java

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY =
            "com.example.android.datasync.provider";
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        /*
         * Create the placeholder account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = CreateSyncAccount(this);
        ...
    }
    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    public void onRefreshButtonClick(View v) {
        // Pass the settings flags by inserting them in a bundle
        Bundle settingsBundle = new Bundle();
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_MANUAL, true);
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
    }