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