Threading in un worker

Quando utilizzi una Worker, WorkManager chiama automaticamente Worker.doWork() in un thread in background. Il thread in background proviene dall'elemento Executor specificato in Configuration di WorkManager. Per impostazione predefinita, WorkManager configura un Executor per te, ma puoi anche personalizzare il tuo. Ad esempio, puoi condividere un esecutore in background esistente nella tua app, creare un Executor con thread singolo per assicurarti che tutto il lavoro in background venga eseguito in sequenza o persino specificare un Executor personalizzato. Per personalizzare Executor, assicurati di inizializzare WorkManager manualmente.

Quando configuri WorkManager manualmente, puoi specificare Executor come segue:

Kotlin

WorkManager.initialize(
    context,
    Configuration.Builder()
         // Uses a fixed thread pool of size 8 threads.
        .setExecutor(Executors.newFixedThreadPool(8))
        .build())

Java

WorkManager.initialize(
    context,
    new Configuration.Builder()
        .setExecutor(Executors.newFixedThreadPool(8))
        .build());

Ecco un esempio di un Worker semplice che scarica i contenuti di una pagina web 100 volte:

Kotlin

class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): ListenableWorker.Result {
        repeat(100) {
            try {
                downloadSynchronously("https://www.google.com")
            } catch (e: IOException) {
                return ListenableWorker.Result.failure()
            }
        }

        return ListenableWorker.Result.success()
    }
}

Java

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        for (int i = 0; i < 100; i++) {
            try {
                downloadSynchronously("https://www.google.com");
            } catch (IOException e) {
                return Result.failure();
            }
        }

        return Result.success();
    }

}

Tieni presente che Worker.doWork() è una chiamata sincrona: devi completare l'intera operazione in background in modo da bloccare e completarla entro il momento in cui il metodo viene eliminato. Se chiami un'API asincrona in doWork() e restituisci un Result, il callback potrebbe non funzionare correttamente. Se ti trovi in questa situazione, valuta la possibilità di utilizzare una ListenableWorker (vedi Threading inListenableWorker).

Quando un dispositivo Worker attualmente in esecuzione viene bloccato per qualsiasi motivo, riceve una chiamata a Worker.onStopped(). Esegui l'override di questo metodo o chiama Worker.isStopped() per eseguire un checkpoint del codice e liberare risorse se necessario. Quando Worker nell'esempio precedente viene interrotto, potrebbe trovarsi nel bel mezzo del suo loop di download di elementi e continuerà a farlo anche se è stato interrotto. Per ottimizzare questo comportamento, puoi procedere nel seguente modo:

Kotlin

class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): ListenableWorker.Result {
        repeat(100) {
            if (isStopped) {
                break
            }

            try {
                downloadSynchronously("https://www.google.com")
            } catch (e: IOException) {
                return ListenableWorker.Result.failure()
            }

        }

        return ListenableWorker.Result.success()
    }
}

Java

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        for (int i = 0; i < 100; ++i) {
            if (isStopped()) {
                break;
            }

            try {
                downloadSynchronously("https://www.google.com");
            } catch (IOException e) {
                return Result.failure();
            }
        }

        return Result.success();
    }
}

Una volta interrotto Worker, non importa cosa torni da Worker.doWork(); il Result verrà ignorato.