Firebase JobDispatcher'dan WorkManager'a taşıma

WorkManager, Android'de ertelenebilir arka plan işlerini planlamak ve yürütmek için kullanılan bir kitaplıktır. Firebase JobDispatcher'ın yerine kullanılması önerilen çözümdür. Aşağıdaki kılavuz, Firebase JobDispatcher uygulamanızı WorkManager'a taşıma sürecinde size yol gösterecektir.

Gradle kurulumu

WorkManager kitaplığını Android projenize aktarmak için WorkManager'ı kullanmaya başlama bölümünde listelenen bağımlılıkları ekleyin.

JobService'ten çalışanlara

FirebaseJobDispatcher, yapılması gereken işleri tanımlamak için giriş noktası olarak JobService alt sınıfını kullanır. JobService doğrudan veya SimpleJobService kullanıyor olabilirsiniz.

JobService öğesi şuna benzer:

Kotlin

import com.firebase.jobdispatcher.JobParameters
import com.firebase.jobdispatcher.JobService

class MyJobService : JobService() {
    override fun onStartJob(job: JobParameters): Boolean {
        // Do some work here
        return false // Answers the question: "Is there still work going on?"
    }
    override fun onStopJob(job: JobParameters): Boolean {
        return false // Answers the question: "Should this job be retried?"
    }
}

Java

import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;

public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters job) {
        // Do some work here

        return false; // Answers the question: "Is there still work going on?"
    }

    @Override
    public boolean onStopJob(JobParameters job) {
        return false; // Answers the question: "Should this job be retried?"
    }
}

SimpleJobService kullanıyorsanız onRunJob() geçersiz kılınmış olur. Bu durumda @JobResult int türü döndürülür.

Aralarındaki temel fark, JobService özelliğini doğrudan kullanmanızdır. onStartJob(), ana iş parçacığında çağrılır ve işi arka plan iş parçacığına yüklemek uygulamanın sorumluluğundadır. Diğer yandan, SimpleJobService kullanıyorsanız bu hizmet, çalışmanızı bir arka plan iş parçacığı üzerinde yürütmekten sorumludur.

WorkManager benzer kavramlara sahiptir. WorkManager'daki temel çalışma birimi ListenableWorker'dir. Worker, RxWorker ve CoroutineWorker (Kotlin eş yordamlarını kullanırken) gibi başka kullanışlı çalışan alt türleri de vardır.

JobService, ListenableWorker ile eşlenir

Doğrudan JobService kullanıyorsanız bu öğenin eşlendiği çalışan ListenableWorker olmalıdır. SimpleJobService kullanıyorsanız bunun yerine Worker kullanmanız gerekir.

Yukarıdaki örneği (MyJobService) kullanarak bunu nasıl ListenableWorker biçimine dönüştürebileceğimize bakalım.

Kotlin

import android.content.Context
import androidx.work.ListenableWorker
import androidx.work.ListenableWorker.Result
import androidx.work.WorkerParameters
import com.google.common.util.concurrent.ListenableFuture

class MyWorker(appContext: Context, params: WorkerParameters) :
    ListenableWorker(appContext, params) {

    override fun startWork(): ListenableFuture<ListenableWorker.Result> {
        // Do your work here.
        TODO("Return a ListenableFuture<Result>")
    }

    override fun onStopped() {
        // Cleanup because you are being stopped.
    }
}

Java

import android.content.Context;
import androidx.work.ListenableWorker;
import androidx.work.ListenableWorker.Result;
import androidx.work.WorkerParameters;
import com.google.common.util.concurrent.ListenableFuture;

class MyWorker extends ListenableWorker {

  public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) {
    super(appContext, params);
  }

  @Override
  public ListenableFuture<ListenableWorker.Result> startWork() {
    // Do your work here.
    Data input = getInputData();

    // Return a ListenableFuture<>
  }

  @Override
  public void onStopped() {
    // Cleanup because you are being stopped.
  }
}

WorkManager'daki temel çalışma birimi ListenableWorker'dir. JobService.onStartJob() gibi, startWork() de ana iş parçacığında çağrılır. Burada MyWorker, ListenableWorker işlemini uygular ve işin tamamlandığını eşzamansız olarak belirtmek için kullanılan bir ListenableFuture örneğini döndürür. Kendi ileti dizisi stratejinizi buradan seçmeniz gerekir.

Buradaki ListenableFuture, sonunda Result.success(), Result.success(Data outputData), Result.retry(), Result.failure() veya Result.failure(Data outputData) olabilecek bir ListenableWorker.Result türü döndürür. Daha fazla bilgi için ListenableWorker.Result referans sayfasını inceleyin.

onStopped(), kısıtlamaların artık karşılanmadığı (örneğin, ağ artık kullanılamadığı için) veya bir WorkManager.cancel…() yöntemi çağrıldığı için ListenableWorker durması gerektiğinin sinyalini vermek için çağrılır. İşletim sistemi herhangi bir nedenle çalışmanızı kapatmaya karar verirse onStopped() de çağrılabilir.

SimpleJobService bir Worker ile eşlenir

SimpleJobService kullanılırken yukarıdaki çalışan şöyle görünür:

Kotlin

import android.content.Context;
import androidx.work.Data;
import androidx.work.ListenableWorker.Result;
import androidx.work.Worker;
import androidx.work.WorkerParameters;


class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        TODO("Return a Result")
    }

    override fun onStopped() {
        super.onStopped()
        TODO("Cleanup, because you are being stopped")
    }
}

Java

import android.content.Context;
import androidx.work.Data;
import androidx.work.ListenableWorker.Result;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

class MyWorker extends Worker {

  public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) {
    super(appContext, params);
  }

  @Override
  public Result doWork() {
    // Do your work here.
    Data input = getInputData();

    // Return a ListenableWorker.Result
    Data outputData = new Data.Builder()
        .putString(“Key”, “value”)
        .build();
    return Result.success(outputData);
  }

  @Override
  public void onStopped() {
    // Cleanup because you are being stopped.
  }
}

Burada doWork(), işin tamamlandığını eşzamanlı olarak belirtmek için ListenableWorker.Result örneğini döndürür. Bu, bir arka plan iş parçacığında işleri planlayan SimpleJobService ile benzerdir.

JobBuilder, WorkRequest ile eşlenir

FirebaseJobBuilder, Job meta verilerini temsil etmek için Job.Builder kullanır. WorkManager, bu rolü doldurmak için WorkRequest değerini kullanır.

WorkManager'da iki tür WorkRequest bulunur: OneTimeWorkRequest ve PeriodicWorkRequest.

Şu anda Job.Builder.setRecurring(true) kullanıyorsanız yeni bir PeriodicWorkRequest oluşturmanız gerekir. Aksi takdirde OneTimeWorkRequest kullanmanız gerekir.

FirebaseJobDispatcher ile karmaşık bir Job planlamasının nasıl olabileceğine bakalım:

Kotlin

val input: Bundle = Bundle().apply {
    putString("some_key", "some_value")
}

val job = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyService::class.java)
    // uniquely identifies the job
    .setTag("my-unique-tag")
    // one-off job
    .setRecurring(false)
    // don't persist past a device reboot
    .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
    // start between 0 and 60 seconds from now
    .setTrigger(Trigger.executionWindow(0, 60))
    // don't overwrite an existing job with the same tag
    .setReplaceCurrent(false)
    // retry with exponential backoff
    .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)

    .setConstraints(
        // only run on an unmetered network
        Constraint.ON_UNMETERED_NETWORK,
        // // only run when the device is charging
        Constraint.DEVICE_CHARGING
    )
    .setExtras(input)
    .build()

dispatcher.mustSchedule(job)

Java

Bundle input = new Bundle();
input.putString("some_key", "some_value");

Job myJob = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyJobService.class)
    // uniquely identifies the job
    .setTag("my-unique-tag")
    // one-off job
    .setRecurring(false)
    // don't persist past a device reboot
    .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
    // start between 0 and 60 seconds from now
    .setTrigger(Trigger.executionWindow(0, 60))
    // don't overwrite an existing job with the same tag
    .setReplaceCurrent(false)
    // retry with exponential backoff
    .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
    // constraints that need to be satisfied for the job to run
    .setConstraints(
        // only run on an unmetered network
        Constraint.ON_UNMETERED_NETWORK,
        // only run when the device is charging
        Constraint.DEVICE_CHARGING
    )
    .setExtras(input)
    .build();

dispatcher.mustSchedule(myJob);

Aynı işlemi WorkManager'da yapabilmek için şunları yapmanız gerekir:

  • Worker için giriş olarak kullanılabilecek giriş verileri oluşturun.
  • Giriş verileri ve yukarıda FirebaseJobDispatcher için tanımlanana benzer kısıtlamalarla bir WorkRequest oluşturun.
  • WorkRequest hattını sıraya alın.

Çalışan için girişleri ayarlama

FirebaseJobDispatcher, giriş verilerini JobService cihazına göndermek için bir Bundle kullanır. WorkManager, bunun yerine Data kullanır. Böylece:

Kotlin

import androidx.work.workDataOf
val data = workDataOf("some_key" to "some_val")

Java

import androidx.work.Data;
Data input = new Data.Builder()
    .putString("some_key", "some_value")
    .build();

Çalışan için Kısıtlamaları Ayarlama

FirebaseJobDispatcher, işlerle ilgili kısıtlamalar ayarlamak için Job.Builder.setConstaints(...) aracını kullanır. WorkManager bunun yerine Constraints kullanır.

Kotlin

import androidx.work.*

val constraints: Constraints = Constraints.Builder().apply {
    setRequiredNetworkType(NetworkType.CONNECTED)
    setRequiresCharging(true)
}.build()

Java

import androidx.work.Constraints;
import androidx.work.Constraints.Builder;
import androidx.work.NetworkType;

Constraints constraints = new Constraints.Builder()
    // The Worker needs Network connectivity
    .setRequiredNetworkType(NetworkType.CONNECTED)
    // Needs the device to be charging
    .setRequiresCharging(true)
    .build();

WorkRequest'i Oluşturma (OneTime veya Periyodik)

OneTimeWorkRequest ve PeriodicWorkRequest oluşturmak için OneTimeWorkRequest.Builder ve PeriodicWorkRequest.Builder kullanmanız gerekir.

Yukarıdaki Job öğesine benzer bir OneTimeWorkRequest oluşturmak için aşağıdakileri yapmanız gerekir:

Kotlin

import androidx.work.*
import java.util.concurrent.TimeUnit

val constraints: Constraints = TODO("Define constraints as above")
val request: OneTimeWorkRequest =
     // Tell which work to execute
     OneTimeWorkRequestBuilder<MyWorker>()
         // Sets the input data for the ListenableWorker
        .setInputData(input)
        // If you want to delay the start of work by 60 seconds
        .setInitialDelay(60, TimeUnit.SECONDS)
        // Set a backoff criteria to be used when retry-ing
        .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS)
        // Set additional constraints
        .setConstraints(constraints)
        .build()

Java

import androidx.work.BackoffCriteria;
import androidx.work.Constraints;
import androidx.work.Constraints.Builder;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.OneTimeWorkRequest.Builder;
import androidx.work.Data;

// Define constraints (as above)
Constraints constraints = ...
OneTimeWorkRequest request =
    // Tell which work to execute
    new OneTimeWorkRequest.Builder(MyWorker.class)
        // Sets the input data for the ListenableWorker
        .setInputData(inputData)
        // If you want to delay the start of work by 60 seconds
        .setInitialDelay(60, TimeUnit.SECONDS)
        // Set a backoff criteria to be used when retry-ing
        .setBackoffCriteria(BackoffCriteria.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS)
        // Set additional constraints
        .setConstraints(constraints)
        .build();

Buradaki en önemli fark, WorkManager işlerinin cihazı yeniden başlatma işleminde her zaman devamlı olmasıdır.

PeriodicWorkRequest oluşturmak istiyorsanız aşağıdaki gibi bir işlem yapmanız gerekir:

Kotlin

val constraints: Constraints = TODO("Define constraints as above")
val request: PeriodicWorkRequest =
PeriodicWorkRequestBuilder<MyWorker>(15, TimeUnit.MINUTES)
    // Sets the input data for the ListenableWorker
    .setInputData(input)
    // Other setters
    .build()

Java

import androidx.work.BackoffCriteria;
import androidx.work.Constraints;
import androidx.work.Constraints.Builder;
import androidx.work.NetworkType;
import androidx.work.PeriodicWorkRequest;
import androidx.work.PeriodicWorkRequest.Builder;
import androidx.work.Data;

// Define constraints (as above)
Constraints constraints = ...

PeriodicWorkRequest request =
    // Executes MyWorker every 15 minutes
    new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES)
        // Sets the input data for the ListenableWorker
        .setInputData(input)
        . // other setters (as above)
        .build();

İşleri programlama

Worker ve WorkRequest tanımladığınıza göre, artık bir çalışma planlamaya hazırsınız.

FirebaseJobDispatcher ile tanımlanan her Job, bir Job'i benzersiz bir şekilde tanımlamak için kullanılan bir tag içeriyordu. Ayrıca, uygulamanın bu Job örneğinin setReplaceCurrent yöntemini çağırarak mevcut Job kopyasıyla değiştirilip değiştirilmeyeceğini de planlayıcıya bildirmesi için bir yol sağladı.

Kotlin

val job = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyService::class.java)
    // uniquely identifies the job
    .setTag("my-unique-tag")
    // don't overwrite an existing job with the same tag
    .setRecurring(false)
    // Other setters...
    .build()

Java

Job myJob = dispatcher.newJobBuilder()
    // the JobService that will be called
    .setService(MyJobService.class)
    // uniquely identifies the job
    .setTag("my-unique-tag")
    // don't overwrite an existing job with the same tag
    .setReplaceCurrent(false)
    // other setters
    // ...

dispatcher.mustSchedule(myJob);

WorkManager kullanırken enqueueUniqueWork() ve enqueueUniquePeriodicWork() API'lerini kullanarak aynı sonucu elde edebilirsiniz (sırasıyla OneTimeWorkRequest ve PeriodicWorkRequest kullanırken). Daha fazla bilgi için WorkManager.enqueueUniqueWork() ve WorkManager.enqueueUniquePeriodicWork() referans sayfalarını inceleyin.

Bu, aşağıdaki gibi bir şeydir:

Kotlin

import androidx.work.*

val request: OneTimeWorkRequest = TODO("A WorkRequest")
WorkManager.getInstance(myContext)
    .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, request)

Java

import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

OneTimeWorkRequest workRequest = // a WorkRequest;
WorkManager.getInstance(myContext)
    // Use ExistingWorkPolicy.REPLACE to cancel and delete any existing pending
    // (uncompleted) work with the same unique name. Then, insert the newly-specified
    // work.
    .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, workRequest);

İş iptal ediliyor

FirebaseJobDispatcher ile çalışmayı iptal etmek için şunları kullanabilirsiniz:

Kotlin

dispatcher.cancel("my-unique-tag")

Java

dispatcher.cancel("my-unique-tag");

WorkManager'ı kullanırken şunları kullanabilirsiniz:

Kotlin

import androidx.work.WorkManager
WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")

Java

import androidx.work.WorkManager;
WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");

WorkManager başlatılıyor

WorkManager genellikle bir ContentProvider kullanarak kendini başlatır. WorkManager'ın düzenleme ve programlama çalışma şekli üzerinde daha fazla kontrole ihtiyacınız varsa WorkManager yapılandırmasını ve başlatmayı özelleştirebilirsiniz.