Android Kotlin Fundamentals 09.2: WorkManager

Questo codelab fa parte del corso Android Kotlin Fundamentals. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Android Kotlin Fundamentals.

Introduzione

La maggior parte delle app reali deve eseguire attività in background di lunga durata. Ad esempio, un'app potrebbe caricare file su un server, sincronizzare i dati da un server e salvarli in un database Room, inviare log a un server o eseguire operazioni costose sui dati. Queste operazioni devono essere eseguite in background, al di fuori del thread UI (thread principale). Le attività in background consumano le risorse limitate di un dispositivo, come RAM e batteria. Se non viene gestita correttamente, l'esperienza utente potrebbe essere negativa.

In questo codelab, imparerai a utilizzare WorkManager per pianificare un'attività in background in modo ottimizzato ed efficiente. Per scoprire di più su altre soluzioni disponibili per l'elaborazione in background su Android, consulta la Guida all'elaborazione in background.

Cosa devi già sapere

  • Come utilizzare i componenti dell'architettura Android ViewModel, LiveData e Room.
  • Come eseguire le trasformazioni su una classe LiveData.
  • Come creare e avviare una coroutine.
  • Come utilizzare gli adattatori di binding nel data binding.
  • Come caricare i dati memorizzati nella cache utilizzando un pattern del repository.

Obiettivi didattici

  • Come creare un Worker, che rappresenta un'unità di lavoro.
  • Come creare un WorkRequest per richiedere l'esecuzione di un lavoro.
  • Come aggiungere vincoli a WorkRequest per definire come e quando deve essere eseguito un worker.
  • Come utilizzare WorkManager per pianificare le attività in background.

In questo lab proverai a:

  • Crea un worker per eseguire un'attività in background per precaricare la playlist di video DevBytes dalla rete.
  • Pianifica l'esecuzione periodica del worker.
  • Aggiungi vincoli a WorkRequest.
  • Pianifica un WorkRequest periodico che viene eseguito una volta al giorno.

In questo codelab, lavorerai sull'app DevBytes che hai sviluppato in un codelab precedente. Se non hai questa app, puoi scaricare il codice iniziale per questa lezione.

L'app DevBytes mostra un elenco di video DevByte, brevi tutorial realizzati dal team di relazioni con gli sviluppatori di Google Android. I video presentano le funzionalità per sviluppatori e le best practice per lo sviluppo di Android.

Migliori l'esperienza utente nell'app precaricando i video una volta al giorno. In questo modo, l'utente riceve contenuti aggiornati non appena apre l'app.

In questa attività scaricherai ed esaminerai il codice iniziale.

Passaggio 1: scarica ed esegui l'app iniziale

Puoi continuare a lavorare con l'app DevBytes che hai creato nel codelab precedente (se ce l'hai). In alternativa, puoi scaricare l'app iniziale.

In questa attività scaricherai ed eseguirai l'app iniziale ed esaminerai il codice iniziale.

  1. Se non hai ancora l'app DevBytes, scarica il codice iniziale di DevBytes per questo codelab dal progetto DevBytesRepository da GitHub.
  2. Decomprimi il codice e apri il progetto in Android Studio.
  3. Connetti il dispositivo di test o l'emulatore a internet, se non è già connesso. Crea ed esegui l'app. L'app recupera l'elenco dei video di DevBytes dalla rete e li visualizza.
  4. Nell'app, tocca un video per aprirlo nell'app YouTube.

Passaggio 2: esplora il codice

L'app iniziale include molto codice introdotto nel codelab precedente. Il codice iniziale di questo codelab include i moduli di rete, interfaccia utente, cache offline e repository. Puoi concentrarti sulla pianificazione dell'attività in background utilizzando WorkManager.

  1. In Android Studio, espandi tutti i pacchetti.
  2. Esplora il pacchetto database. Il pacchetto contiene le entità del database e il database locale, implementato utilizzando Room.
  3. Esplora il pacchetto repository. Il pacchetto contiene la classe VideosRepository che astrae il livello dati dal resto dell'app.
  4. Esplora il resto del codice iniziale in autonomia e con l'aiuto del codelab precedente.

WorkManager è uno dei componenti dell'architettura Android e fa parte di Android Jetpack. WorkManager è per il lavoro in background rinviabile che richiede l'esecuzione garantita:

  • Differibile significa che il lavoro non deve essere eseguito immediatamente. Ad esempio, l'invio di dati analitici al server o la sincronizzazione del database in background sono attività che possono essere posticipate.
  • Esecuzione garantita significa che l'attività verrà eseguita anche se l'app si chiude o il dispositivo si riavvia.

Mentre WorkManager esegue il lavoro in background, si occupa dei problemi di compatibilità e delle best practice per la batteria e l'integrità del sistema. WorkManager offre la compatibilità fino al livello API 14. WorkManager sceglie un modo appropriato per pianificare un'attività in background, a seconda del livello API del dispositivo. Potrebbe utilizzare JobScheduler (su API 23 e successive) o una combinazione di AlarmManager e BroadcastReceiver.

WorkManager ti consente anche di impostare i criteri per l'esecuzione dell'attività in background. Ad esempio, potresti voler eseguire l'attività solo quando lo stato della batteria, lo stato della rete o lo stato di carica soddisfano determinati criteri. Imparerai a impostare i vincoli più avanti in questo codelab.

In questo codelab, pianifichi un'attività per precaricare la playlist di video DevBytes dalla rete una volta al giorno. Per pianificare questa attività, utilizzi la libreria WorkManager.

  1. Apri il file build.gradle (Module:app) e aggiungi la dipendenza WorkManager al progetto.

    Se utilizzi l'ultima versione della libreria, la compilazione dell'app di soluzione dovrebbe avvenire come previsto. In caso contrario, prova a risolvere il problema o ripristina la versione della libreria mostrata di seguito.
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. Sincronizza il progetto e assicurati che non siano presenti errori di compilazione.

Prima di aggiungere codice al progetto, acquisisci familiarità con le seguenti classi nella libreria WorkManager:

  • Worker
    Questa classe definisce il lavoro effettivo (l'attività) da eseguire in background. Estendi questa classe e sostituisci il metodo doWork(). Il metodo doWork() è quello in cui inserisci il codice da eseguire in background, ad esempio la sincronizzazione dei dati con il server o l'elaborazione delle immagini. In questa attività implementerai Worker.
  • WorkRequest
    Questa classe rappresenta una richiesta di esecuzione del worker in background. Utilizza WorkRequest per configurare come e quando eseguire l'attività worker, con l'aiuto di Constraints come il dispositivo collegato o la connessione Wi-Fi. Implementerai WorkRequest in un'attività successiva.
  • WorkManager
    Questa classe pianifica ed esegue il tuo WorkRequest. WorkManager pianifica le richieste di lavoro in modo da distribuire il carico sulle risorse di sistema, rispettando i vincoli specificati. Implementerai WorkManager in un'attività successiva.

Passaggio 1: crea un lavoratore

In questa attività, aggiungi un Worker per precaricare in background la playlist di video DevBytes.

  1. All'interno del pacchetto devbyteviewer, crea un nuovo pacchetto denominato work.
  2. All'interno del pacchetto work, crea una nuova classe Kotlin denominata RefreshDataWorker.
  3. Estendi il corso RefreshDataWorker dal corso CoroutineWorker. Inserisci context e WorkerParameters come parametri del costruttore.
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. Per risolvere l'errore della classe astratta, esegui l'override del metodo doWork() all'interno della classe RefreshDataWorker.
override suspend fun doWork(): Result {
  return Result.success()
}

Una funzione di sospensione è una funzione che può essere sospesa e ripresa in un secondo momento. Una funzione di sospensione può eseguire un'operazione a esecuzione prolungata e attendere il completamento senza bloccare il thread principale.

Passaggio 2: implementa doWork()

Il metodo doWork() all'interno della classe Worker viene chiamato su un thread in background. Il metodo esegue il lavoro in modo sincrono e deve restituire un oggetto ListenableWorker.Result. Il sistema Android concede a un Worker un massimo di 10 minuti per completare l'esecuzione e restituire un oggetto ListenableWorker.Result. Trascorso questo periodo di tempo, il sistema interrompe forzatamente Worker.

Per creare un oggetto ListenableWorker.Result, chiama uno dei seguenti metodi statici per indicare lo stato di completamento del lavoro in background:

In questa attività implementerai il metodo doWork() per recuperare la playlist di video DevBytes dalla rete. Puoi riutilizzare i metodi esistenti nella classe VideosRepository per recuperare i dati dalla rete.

  1. Nella classe RefreshDataWorker, all'interno di doWork(), crea e istanzia un oggetto VideosDatabase e un oggetto VideosRepository.
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. Nella classe RefreshDataWorker, all'interno di doWork(), sopra l'istruzione return, chiama il metodo refreshVideos() all'interno di un blocco try. Aggiungi un log per monitorare quando viene eseguito il worker.
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

Per risolvere l'errore "Riferimento non risolto", importa retrofit2.HttpException.

  1. Ecco la classe RefreshDataWorker completa per riferimento:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Un Worker definisce un'unità di lavoro e il WorkRequest definisce come e quando deve essere eseguito il lavoro. Esistono due implementazioni concrete della classe WorkRequest:

  • Il corso OneTimeWorkRequest è per le attività una tantum. Un'attività una tantum viene eseguita una sola volta.
  • Il corso PeriodicWorkRequest è destinato a lavori periodici, che si ripetono a intervalli.

Le attività possono essere una tantum o periodiche, quindi scegli il corso di conseguenza. Per saperne di più sulla pianificazione del lavoro ricorrente, consulta la documentazione sul lavoro ricorrente.

In questa attività, definisci e pianifichi un WorkRequest per eseguire il worker che hai creato nell'attività precedente.

Passaggio 1: configura il lavoro ricorrente

All'interno di un'app per Android, la classe Application è la classe base che contiene tutti gli altri componenti, come attività e servizi. Quando viene creato il processo per l'applicazione o il pacchetto, la classe Application (o qualsiasi sottoclasse di Application) viene istanziata prima di qualsiasi altra classe.

In questa app di esempio, la classe DevByteApplication è una sottoclasse della classe Application. La lezione di DevByteApplication è un buon momento per programmare la WorkManager.

  1. Nella classe DevByteApplication, crea un metodo chiamato setupRecurringWork() per configurare il lavoro in background ricorrente.
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. All'interno del metodo setupRecurringWork(), crea e inizializza una richiesta di lavoro periodica da eseguire una volta al giorno utilizzando il metodo PeriodicWorkRequestBuilder(). Inserisci la classe RefreshDataWorker che hai creato nell'attività precedente. Passa un intervallo di ripetizione di 1 con un'unità di tempo di TimeUnit.DAYS.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

Per risolvere l'errore, importa java.util.concurrent.TimeUnit.

Passaggio 2: pianifica una WorkRequest con WorkManager

Dopo aver definito il WorkRequest, puoi pianificarlo con WorkManager utilizzando il metodo enqueueUniquePeriodicWork(). Questo metodo consente di aggiungere alla coda un PeriodicWorkRequest con un nome univoco, in cui può essere attivo un solo PeriodicWorkRequest con un determinato nome alla volta.

Ad esempio, potresti voler attivare una sola operazione di sincronizzazione. Se un'operazione di sincronizzazione è in attesa, puoi scegliere di eseguirla o sostituirla con il tuo nuovo lavoro utilizzando un ExistingPeriodicWorkPolicy.

Per scoprire di più sui modi per pianificare un WorkRequest, consulta la documentazione di WorkManager.

  1. Nella classe RefreshDataWorker, all'inizio della classe, aggiungi un oggetto complementare. Definisci un nome del worker per identificarlo in modo univoco.
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. Nella classe DevByteApplication, alla fine del metodo setupRecurringWork(), pianifica il lavoro utilizzando il metodo enqueueUniquePeriodicWork(). Passa l'enumerazione KEEP per ExistingPeriodicWorkPolicy. Inserisci repeatingRequest come parametro PeriodicWorkRequest.
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

Se esiste un lavoro in attesa (non completato) con lo stesso nome, il parametro ExistingPeriodicWorkPolicy.KEEP fa sì che WorkManager mantenga il lavoro periodico precedente e scarti la nuova richiesta di lavoro.

  1. All'inizio della classe DevByteApplication, crea un oggetto CoroutineScope. Inserisci Dispatchers.Default come parametro del costruttore.
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. Nella classe DevByteApplication, aggiungi un nuovo metodo chiamato delayedInit() per avviare una coroutine.
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. All'interno del metodo delayedInit(), chiama setupRecurringWork().
  2. Sposta l'inizializzazione di Timber dal metodo onCreate() al metodo delayedInit().
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. Nella classe DevByteApplication, alla fine del metodo onCreate(), aggiungi una chiamata al metodo delayedInit().
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. Apri il riquadro Logcat nella parte inferiore della finestra di Android Studio. Filtra in base a RefreshDataWorker.
  2. Esegui l'app. WorkManager pianifica immediatamente il lavoro ricorrente.

    Nel riquadro Logcat, nota le istruzioni di log che mostrano che la richiesta di lavoro è pianificata e poi viene eseguita correttamente.
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Il log WM-WorkerWrapper viene visualizzato dalla libreria WorkManager, quindi non puoi modificare questo messaggio di log.

Passaggio 3: (facoltativo) pianifica la richiesta di lavoro per un intervallo minimo

In questo passaggio, riduci l'intervallo di tempo da 1 giorno a 15 minuti. In questo modo, puoi vedere i log di una richiesta di lavoro periodica in azione.

  1. Nella classe DevByteApplication, all'interno del metodo setupRecurringWork(), commenta la definizione corrente di repeatingRequest. Aggiungi una nuova richiesta di lavoro con un intervallo di ripetizione periodica di 15 minuti.
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. Apri il riquadro Logcat in Android Studio e filtra in base a RefreshDataWorker. Per cancellare i log precedenti, fai clic sull'icona Cancella logcat .
  2. Esegui l'app e WorkManager pianifica immediatamente il lavoro ricorrente. Nel riquadro Logcat, nota i log: la richiesta di lavoro viene eseguita una volta ogni 15 minuti. Attendi 15 minuti per visualizzare un altro insieme di log delle richieste di lavoro. Puoi lasciare l'app in esecuzione o chiuderla; il gestore delle attività dovrebbe comunque essere in esecuzione.

    Tieni presente che l'intervallo a volte è inferiore a 15 minuti e a volte superiore a 15 minuti. (La tempistica esatta è soggetta alle ottimizzazioni della batteria del sistema operativo).
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

Complimenti! Hai creato un lavoratore e pianificato la richiesta di lavoro con WorkManager. Ma c'è un problema: non hai specificato alcun vincolo. WorkManager pianificherà il lavoro una volta al giorno, anche se la batteria del dispositivo è in esaurimento, il dispositivo è in modalità Sospensione o non è connesso a una rete. Ciò influirà sulla batteria e sulle prestazioni del dispositivo e potrebbe comportare un'esperienza utente scadente.

Nell'attività successiva, risolverai questo problema aggiungendo vincoli.

Nell'attività precedente, hai utilizzato WorkManager per pianificare una richiesta di lavoro. In questa attività aggiungi i criteri per l'esecuzione del lavoro.

Quando definisci WorkRequest, puoi specificare i vincoli per il momento in cui deve essere eseguito Worker. Ad esempio, potresti voler specificare che l'attività deve essere eseguita solo quando il dispositivo è inattivo o solo quando è collegato e connesso alla rete Wi-Fi. Puoi anche specificare una policy di backoff per riprovare a eseguire il lavoro. I vincoli supportati sono i metodi impostati in Constraints.Builder. Per scoprire di più, consulta Definire le richieste di lavoro.

Passaggio 1: aggiungi un oggetto Constraints e imposta un vincolo

In questo passaggio, crei un oggetto Constraints e imposti un vincolo sull'oggetto, un vincolo di tipo di rete. È più facile notare i log con un solo vincolo. In un passaggio successivo, aggiungi altri vincoli.)

  1. Nella classe DevByteApplication, all'inizio di setupRecurringWork(), definisci un val di tipo Constraints. Utilizza il metodo Constraints.Builder().
val constraints = Constraints.Builder()

Per risolvere l'errore, importa androidx.work.Constraints.

  1. Utilizza il metodo setRequiredNetworkType() per aggiungere un vincolo di tipo rete all'oggetto constraints. Utilizza l'enumerazione UNMETERED in modo che la richiesta di lavoro venga eseguita solo quando il dispositivo è connesso a una rete non a consumo.
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. Utilizza il metodo build() per generare i vincoli dal generatore.
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

Ora devi impostare l'oggetto Constraints appena creato sulla richiesta di lavoro.

  1. Nella classe DevByteApplication, all'interno del metodo setupRecurringWork(), imposta l'oggetto Constraints sulla richiesta di lavoro periodica, repeatingRequest. Per impostare i vincoli, aggiungi il metodo setConstraints() sopra la chiamata al metodo build().
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

Passaggio 2: esegui l'app e prendi nota dei log

In questo passaggio, esegui l'app e noti che la richiesta di lavoro vincolata viene eseguita in background a intervalli.

  1. Disinstalla l'app dal dispositivo o dall'emulatore per annullare le attività pianificate in precedenza.
  2. Apri il riquadro Logcat in Android Studio. Nel riquadro Logcat, cancella i log precedenti facendo clic sull'icona Cancella logcat a sinistra. Filtra in base a work.
  3. Disattiva il Wi-Fi nel dispositivo o nell'emulatore per vedere come funzionano i vincoli. Il codice attuale imposta un solo vincolo, indicando che la richiesta deve essere eseguita solo su una rete senza limiti. Poiché il Wi-Fi è disattivato, il dispositivo non è connesso alla rete, a consumo o senza limiti. Pertanto, questo vincolo non verrà soddisfatto.
  4. Esegui l'app e nota il riquadro Logcat. WorkManager pianifica immediatamente l'attività in background. Poiché il vincolo di rete non è soddisfatto, l'attività non viene eseguita.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. Attiva il Wi-Fi nel dispositivo o nell'emulatore e guarda il riquadro Logcat. Ora l'attività in background pianificata viene eseguita circa ogni 15 minuti, a condizione che il vincolo di rete sia soddisfatto.
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

Passaggio 3: aggiungi altri vincoli

In questo passaggio, aggiungi i seguenti vincoli a PeriodicWorkRequest:

  • Batteria non scarica.
  • Ricarica del dispositivo.
  • Dispositivo inattivo; disponibile solo nel livello API 23 (Android M) e versioni successive.

Implementa quanto segue nella classe DevByteApplication.

  1. Nella classe DevByteApplication, all'interno del metodo setupRecurringWork(), indica che la richiesta di lavoro deve essere eseguita solo se la batteria non è in esaurimento. Aggiungi il vincolo prima della chiamata al metodo build() e utilizza il metodo setRequiresBatteryNotLow().
.setRequiresBatteryNotLow(true)
  1. Aggiorna la richiesta di lavoro in modo che venga eseguita solo quando il dispositivo è in carica. Aggiungi il vincolo prima della chiamata al metodo build() e utilizza il metodo setRequiresCharging().
.setRequiresCharging(true)
  1. Aggiorna la richiesta di lavoro in modo che venga eseguita solo quando il dispositivo è inattivo. Aggiungi il vincolo prima della chiamata al metodo build() e utilizza il metodo setRequiresDeviceIdle(). Questo vincolo esegue la richiesta di lavoro solo quando l'utente non sta utilizzando attivamente il dispositivo. Questa funzionalità è disponibile solo in Android 6.0 (Marshmallow) e versioni successive, quindi aggiungi una condizione per la versione SDK M e successive.
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

Ecco la definizione completa dell'oggetto constraints.

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. All'interno del metodo setupRecurringWork(), modifica l'intervallo di richiesta in una volta al giorno.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

Ecco l'implementazione completa del metodo setupRecurringWork(), con un log per monitorare la pianificazione della richiesta di lavoro periodica.

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. Per rimuovere la richiesta di lavoro pianificata in precedenza, disinstalla l'app DevBytes dal dispositivo o dall'emulatore.
  2. Esegui l'app e WorkManager pianifica immediatamente la richiesta di lavoro. La richiesta di lavoro viene eseguita una volta al giorno, quando vengono soddisfatti tutti i vincoli.
  3. Questa richiesta di lavoro verrà eseguita in background finché l'app è installata, anche se non è in esecuzione. Per questo motivo, devi disinstallare l'app dallo smartphone.

Ottimo lavoro! Hai implementato e pianificato una richiesta di lavoro a basso consumo di batteria per il precaricamento giornaliero dei video nell'app DevBytes. WorkManager pianificherà ed eseguirà il lavoro, ottimizzando le risorse di sistema. I tuoi utenti e le loro batterie saranno molto felici.

Progetto Android Studio: DevBytesWorkManager.

  • L'API WorkManager semplifica la pianificazione di attività asincrone differibili che devono essere eseguite in modo affidabile.
  • La maggior parte delle app reali deve eseguire attività in background di lunga durata. Per pianificare un'attività in background in modo ottimizzato ed efficiente, utilizza WorkManager.
  • Le classi principali della libreria WorkManager sono Worker, WorkRequest e WorkManager.
  • La classe Worker rappresenta un'unità di lavoro. Per implementare l'attività in background, estendi la classe Worker e sostituisci il metodo doWork().
  • La classe WorkRequest rappresenta una richiesta di esecuzione di un'unità di lavoro. WorkRequest è la classe base per specificare i parametri per il lavoro che pianifichi in WorkManager.
  • Esistono due implementazioni concrete della classe WorkRequest: OneTimeWorkRequest per le attività una tantum e PeriodicWorkRequest per le richieste di lavoro periodiche.
  • Quando definisci WorkRequest, puoi specificare Constraints che indica quando deve essere eseguito Worker. I vincoli includono elementi come se il dispositivo è collegato alla corrente, se è inattivo o se è connesso al Wi-Fi.
  • Per aggiungere vincoli a WorkRequest, utilizza i metodi di impostazione elencati nella documentazione di Constraints.Builder. Ad esempio, per indicare che WorkRequest non deve essere eseguito se la batteria del dispositivo è scarica, utilizza il metodo set setRequiresBatteryNotLow().
  • Dopo aver definito l'WorkRequest, delega l'attività al sistema Android. Per farlo, pianifica l'attività utilizzando uno dei WorkManager metodi enqueue.
  • L'ora esatta in cui viene eseguito il Worker dipende dai vincoli utilizzati nel WorkRequest e dalle ottimizzazioni del sistema. WorkManager è progettato per offrire il miglior comportamento possibile, tenendo conto di queste limitazioni.

Corso Udacity:

Documentazione per sviluppatori Android:

Altro:

Questa sezione elenca i possibili compiti a casa per gli studenti che seguono questo codelab nell'ambito di un corso guidato da un insegnante. Spetta all'insegnante:

  • Assegna i compiti, se richiesto.
  • Comunica agli studenti come inviare i compiti.
  • Valuta i compiti a casa.

Gli insegnanti possono utilizzare questi suggerimenti nella misura che ritengono opportuna e sono liberi di assegnare qualsiasi altro compito a casa che ritengono appropriato.

Se stai seguendo questo codelab in autonomia, sentiti libero di utilizzare questi compiti per casa per mettere alla prova le tue conoscenze.

Domanda 1

Quali sono le implementazioni concrete della classe WorkRequest?

OneTimeWorkPeriodicRequest

OneTimeWorkRequest e PeriodicWorkRequest

OneTimeWorkRequest e RecurringWorkRequest

OneTimeOffWorkRequest e RecurringWorkRequest

Domanda 2

Quale delle seguenti classi utilizza WorkManager per pianificare l'attività in background sull'API 23 e versioni successive?

▢ Solo JobScheduler

BroadcastReceiver e AlarmManager

AlarmManager e JobScheduler

Scheduler e BroadcastReceiver

Domanda 3

Quale API utilizzi per aggiungere vincoli a un WorkRequest?

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

Inizia la lezione successiva: 10.1 Stili e temi

Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab di Android Kotlin Fundamentals.