Esegui un adattatore di sincronizzazione

Nota: consigliamo WorkManager consigliata per la maggior parte dei casi d'uso relativi all'elaborazione in background. Fai riferimento alle guida all'elaborazione in background per scoprire quale soluzione funziona meglio per te.

Nelle lezioni precedenti di questo corso hai imparato a creare un componente adattatore di sincronizzazione che incapsula il codice di trasferimento dei dati e spiega come aggiungere i componenti collega l'adattatore di sincronizzazione al sistema. Ora hai tutto ciò che ti serve per installare un'app che include un adattatore di sincronizzazione, ma nessuno del codice che hai visto è in grado di eseguire l'adattatore di sincronizzazione.

Dovresti provare a eseguire l'adattatore di sincronizzazione in base a una pianificazione o come risultato indiretto di . Ad esempio, se desideri che l'adattatore di sincronizzazione venga eseguito regolarmente, dopo un in un determinato periodo di tempo o in un'ora specifica del giorno. Potresti anche voler eseguire la sincronizzazione quando vengono apportate modifiche ai dati memorizzati sul dispositivo. Dovresti evitare di eseguire l'adattatore di sincronizzazione come risultato diretto di un'azione dell'utente, perché così facendo non si ottiene il vantaggio della capacità di pianificazione del framework dell'adattatore di sincronizzazione. Ad esempio, dovresti evitare fornendo un pulsante di aggiornamento nell'interfaccia utente.

Sono disponibili le seguenti opzioni per l'esecuzione dell'adattatore di sincronizzazione:

Quando i dati del server cambiano
Esegui l'adattatore di sincronizzazione in risposta a un messaggio da un server, che indica che l'analisi dei dati è cambiato. Questa opzione ti consente di aggiornare i dati dal server al dispositivo senza ridurre le prestazioni o sprecare la durata della batteria tramite il polling del server.
Quando i dati del dispositivo cambiano
Esegui un adattatore di sincronizzazione quando i dati cambiano sul dispositivo. Questa opzione ti consente di inviare dati modificati dal dispositivo a un server ed è particolarmente utile se devi verificare in modo che il server disponga sempre dei dati del dispositivo più recenti. Questa opzione è molto semplice implementare se memorizzi effettivamente i dati nel tuo fornitore di contenuti. Se utilizzi uno stub fornitore di contenuti, il rilevamento delle modifiche ai dati potrebbe essere più difficile.
A intervalli regolari
Esegui un adattatore di sincronizzazione dopo la scadenza dell'intervallo scelto o a un certo punto volta ogni giorno.
On demand
Esegui l'adattatore di sincronizzazione in risposta a un'azione dell'utente. Tuttavia, per fornire all'utente dovresti fare affidamento principalmente su una delle opzioni più automatizzate. Utilizzando e le opzioni automatizzate consentono di risparmiare batteria e risorse di rete.

Il resto di questa lezione descrive ciascuna delle opzioni in maggiore dettaglio.

Esegui l'adattatore di sincronizzazione quando i dati del server cambiano

Se la tua app trasferisce i dati da un server e i dati del server cambiano spesso, puoi utilizzare un adattatore di sincronizzazione per eseguire download in risposta a modifiche dei dati. Per eseguire l'adattatore di sincronizzazione, devi il server invia un messaggio speciale a BroadcastReceiver nella tua app. In risposta a questo messaggio, chiama ContentResolver.requestSync() per segnalare al framework dell'adattatore di sincronizzazione di eseguire il tuo l'adattatore di sincronizzazione.

Google Cloud Messaging (GCM) fornisce sia le i componenti di server e dispositivo necessari per il funzionamento di questo sistema di messaggistica. Utilizzo di GCM per l'attivazione è più affidabile ed efficiente rispetto al polling dei server per verificare lo stato. Durante il sondaggio richiede un Service sempre attivo, GCM utilizza un BroadcastReceiver che viene attivata quando arriva un messaggio. Durante il sondaggio a intervalli regolari utilizza la batteria anche se non ci sono aggiornamenti disponibili, GCM invia solo quando serve.

Nota:se utilizzi GCM per attivare l'adattatore di sincronizzazione tramite una trasmissione a tutti dispositivi su cui è installata la tua app, ricorda che ricevono il tuo messaggio all'indirizzo più o meno contemporaneamente. Questa situazione può causare l'esecuzione di più istanze dell'adattatore di sincronizzazione causando contemporaneamente il sovraccarico del server e della rete. Per evitare questa situazione per una trasmissione a tutti i dispositivi, è consigliabile posticipare l'avvio dell'adattatore di sincronizzazione per un certo periodo univoco per ogni dispositivo.

Il seguente snippet di codice mostra come eseguire requestSync() in risposta a un messaggio GCM in arrivo:

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

Esegui l'adattatore di sincronizzazione quando i dati del fornitore di contenuti cambiano

Se la tua app raccoglie dati in un fornitore di contenuti e vuoi aggiornare il server ogni volta aggiorni il provider, puoi configurare la tua app in modo che esegua automaticamente l'adattatore di sincronizzazione. Da fare In questo caso, registri un osservatore per il fornitore di contenuti. Quando i dati nel tuo fornitore di contenuti modifiche, il framework del fornitore di contenuti chiama l'osservatore. Nell'osservatore, richiama requestSync() per indicare l'esecuzione del framework l'adattatore di sincronizzazione.

Nota. Se utilizzi un fornitore di contenuti stub, non sono presenti dati in il fornitore di contenuti e onChange() sta non ho mai chiamato. In questo caso, devi fornire un tuo meccanismo di rilevamento delle modifiche dati del dispositivo. Questo meccanismo è responsabile anche requestSync() quando i dati cambiano.

Per creare un osservatore per il tuo fornitore di contenuti, estendi il corso ContentObserver e implementare entrambe le forme Metodo onChange(). Nella onChange(), chiama requestSync() per avviare l'adattatore di sincronizzazione.

Per registrare l'osservatore, passalo come argomento in una chiamata a registerContentObserver(). Nella devi passare anche un URI dei contenuti per i dati che vuoi guardare. I contenuti il framework del provider confronta questo URI di visualizzazione con gli URI dei contenuti passati come argomenti ContentResolver metodi che modificano il tuo fornitore, ad esempio ContentResolver.insert(). Se viene rilevata una corrispondenza, implementazione di ContentObserver.onChange() .

Il seguente snippet di codice mostra come definire un valore ContentObserver che chiama requestSync() quando una tabella modifiche:

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

Esegui periodicamente l'adattatore di sincronizzazione

Puoi eseguire periodicamente l'adattatore di sincronizzazione impostando un periodo di tempo di attesa tra le esecuzioni, o eseguendolo solo in determinate ore della giornata. Esecuzione dell'adattatore di sincronizzazione ti consente di ottenere periodicamente una corrispondenza approssimativa con l'intervallo di aggiornamento del server.

Analogamente, puoi caricare i dati dal dispositivo quando il server è relativamente inattivo. pianificazione dell'esecuzione notturna dell'adattatore di sincronizzazione. La maggior parte degli utenti lascia il dispositivo acceso e collegato alla corrente di notte, quindi solitamente è disponibile. Inoltre, il dispositivo non esegue altre attività in e l'adattatore di sincronizzazione. Se scegli questo approccio, tuttavia, devi assicurarti che ogni dispositivo attiva un trasferimento di dati in momenti leggermente diversi. Se tutti i dispositivi supportano l'adattatore di sincronizzazione, è probabile che sovraccarichi i dati del server e del provider di telefonia mobile reti.

In generale, le esecuzioni periodiche hanno senso se gli utenti non hanno bisogno di aggiornamenti istantanei, ma si aspettano di aggiornamenti regolari. Le esecuzioni periodiche hanno senso anche se vuoi bilanciare la disponibilità i dati aggiornati grazie all'efficienza grazie alle piccole esecuzioni degli adattatori di sincronizzazione senza un uso eccessivo del dispositivo Google Cloud.

Per eseguire l'adattatore di sincronizzazione a intervalli regolari, chiama addPeriodicSync(). In questo modo vengono programmati di sincronizzazione dell'adattatore in modo che venga eseguito una volta trascorso un certo periodo di tempo. Poiché il framework degli adattatori di sincronizzazione deve tenere conto di altre esecuzioni dell'adattatore di sincronizzazione e cerca di massimizzare l'efficienza della batteria, il tempo trascorso potrebbe variare di alcuni secondi. Inoltre, il framework non esegue l'adattatore di sincronizzazione se di rete non disponibile.

Nota che addPeriodicSync() non a una determinata ora del giorno. Per far funzionare l'adattatore di sincronizzazione all'incirca alla alla stessa ora ogni giorno, usa una sveglia ripetuta come attivatore. La ripetizione delle sveglie è descritta in maggiore dettagli nella documentazione di riferimento per AlarmManager. Se utilizzi metodo setInexactRepeating() da impostare attivatori ora del giorno che presentano alcune variazioni, devi comunque randomizzare l'ora di inizio assicurati che l'adattatore di sincronizzazione venga eseguito da dispositivi diversi sfalsati.

Il metodo addPeriodicSync() non disattiva setSyncAutomatically(), pertanto potresti ricevere più sincronizzazioni in un periodo di tempo relativamente breve. Inoltre, solo alcune i flag di controllo dell'adattatore di sincronizzazione sono consentiti in una chiamata a addPeriodicSync(); i flag non consentiti sono descritti nella documentazione di riferimento per addPeriodicSync().

Il seguente snippet di codice mostra come pianificare le esecuzioni periodiche dell'adattatore di sincronizzazione:

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

Esegui l'adattatore di sincronizzazione on demand

L'esecuzione dell'adattatore di sincronizzazione in risposta alla richiesta di un utente è la strategia meno preferibile per eseguire un adattatore di sincronizzazione. La struttura è progettata appositamente per risparmiare batteria quando esegue adattatori di sincronizzazione in base a una pianificazione. Opzioni che eseguono una sincronizzazione in risposta ai dati le modifiche utilizzano la carica della batteria in modo efficace, poiché la corrente viene utilizzata per fornire nuovi dati.

Invece, consentire agli utenti di eseguire una sincronizzazione on demand significa che la sincronizzazione funziona da sola, è un uso inefficiente della rete e delle risorse energetiche. Inoltre, fornire il servizio di sincronizzazione on demand porta gli utenti richiedere una sincronizzazione anche se non ci sono prove che i dati siano cambiati ed eseguire una sincronizzazione che l'aggiornamento dei dati è un uso inefficace della batteria. In generale, l'app dovrebbe: Utilizzare altri indicatori per attivare una sincronizzazione o pianificarli a intervalli regolari, senza input utente.

Tuttavia, se vuoi comunque eseguire l'adattatore di sincronizzazione on demand, imposta i flag dell'adattatore di sincronizzazione per un manualmente l'esecuzione dell'adattatore di sincronizzazione, quindi richiama ContentResolver.requestSync().

Esegui trasferimenti on demand con i seguenti flag:

SYNC_EXTRAS_MANUAL
Forza una sincronizzazione manuale. Il framework dell'adattatore di sincronizzazione ignora le impostazioni esistenti, come il flag impostato da setSyncAutomatically().
SYNC_EXTRAS_EXPEDITED
Forza l'avvio immediato della sincronizzazione. Se non imposti questa opzione, il sistema potrebbe attendere diversi secondi prima di eseguire la richiesta di sincronizzazione perché tenta di ottimizzare l'utilizzo della batteria pianificare molte richieste in un breve periodo di tempo.

Il seguente snippet di codice mostra come chiamare requestSync() in risposta a un pulsante clic:

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