مهاجرت از Firebase JobDispatcher به WorkManager

WorkManager کتابخانه ای برای زمان بندی و اجرای کارهای پس زمینه قابل تعویق در اندروید است. این جایگزین پیشنهادی برای Firebase JobDispatcher است. راهنمای زیر شما را در فرآیند انتقال پیاده‌سازی Firebase JobDispatcher به WorkManager راهنمایی می‌کند.

راه اندازی Gradle

برای وارد کردن کتابخانه WorkManager به پروژه Android خود، وابستگی‌های فهرست شده در شروع با WorkManager را اضافه کنید.

از JobService تا کارگران

FirebaseJobDispatcher از یک زیر کلاس JobService به عنوان نقطه ورودی برای تعریف کاری که باید انجام شود استفاده می کند. ممکن است به طور مستقیم JobService یا از SimpleJobService استفاده کنید.

JobService چیزی شبیه به این خواهد بود:

کاتلین

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?"
    }
}

جاوا

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 استفاده می کنید onRunJob() باطل کرده اید که یک نوع @JobResult int را برمی گرداند.

تفاوت اصلی این است که وقتی مستقیماً از JobService استفاده می کنید، onStartJob() در رشته اصلی فراخوانی می شود و این وظیفه برنامه است که کار را در یک رشته پس زمینه بارگذاری کند. از طرف دیگر، اگر از SimpleJobService استفاده می کنید، آن سرویس وظیفه اجرای کار شما را بر روی یک رشته پس زمینه دارد.

WorkManager مفاهیم مشابهی دارد. واحد اساسی کار در WorkManager یک ListenableWorker است. همچنین زیرشاخه‌های مفید دیگری از کارگران مانند Worker ، RxWorker ، و CoroutineWorker (هنگام استفاده از کوروتین‌های Kotlin) وجود دارد.

JobService به یک ListenableWorker نگاشت می شود

اگر مستقیماً از JobService استفاده می‌کنید، کارگری که به آن نقشه‌برداری می‌کند ListenableWorker است. اگر از SimpleJobService استفاده می کنید، به جای آن باید از Worker استفاده کنید.

بیایید از مثال بالا ( MyJobService ) استفاده کنیم و ببینیم چگونه می توانیم آن را به ListenableWorker تبدیل کنیم.

کاتلین

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

جاوا

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 یک ListenableWorker است. درست مانند JobService.onStartJob() ، startWork() در رشته اصلی فراخوانی می شود. در اینجا MyWorker ListenableWorker را پیاده‌سازی می‌کند و نمونه‌ای از ListenableFuture را برمی‌گرداند، که برای سیگنال دادن به اتمام کار به صورت ناهمزمان استفاده می‌شود. شما باید استراتژی threading خود را در اینجا انتخاب کنید.

ListenableFuture در اینجا در نهایت یک نوع ListenableWorker.Result را برمی گرداند که می تواند یکی از Result.success() , Result.success(Data outputData) Result.retry() , Result.failure() یا Result.failure(Data outputData) باشد. برای اطلاعات بیشتر، صفحه مرجع ListenableWorker.Result را ببینید.

onStopped() فراخوانی می شود تا سیگنالی باشد که ListenableWorker باید متوقف شود، یا به این دلیل که محدودیت ها دیگر برآورده نمی شوند (مثلاً به دلیل اینکه شبکه دیگر در دسترس نیست)، یا به این دلیل که متد WorkManager.cancel…() فراخوانی شده است. اگر سیستم عامل به دلایلی تصمیم بگیرد کار شما را خاموش کند onStopped() نیز ممکن است فراخوانی شود.

SimpleJobService به یک Worker نقشه می‌دهد

هنگام استفاده از SimpleJobService ، کارگر فوق به شکل زیر خواهد بود:

کاتلین

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

جاوا

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

در اینجا doWork() نمونه‌ای از ListenableWorker.Result را به سیگنال تکمیل کار همزمان برمی‌گرداند. این شبیه به SimpleJobService است که کارها را در یک رشته پس‌زمینه زمان‌بندی می‌کند.

نقشه های JobBuilder به WorkRequest

FirebaseJobBuilder از Job.Builder برای نمایش فراداده Job استفاده می کند. WorkManager از WorkRequest برای پر کردن این نقش استفاده می کند.

WorkManager دو نوع WorkRequest دارد: OneTimeWorkRequest و PeriodicWorkRequest .

اگر در حال حاضر از Job.Builder.setRecurring(true) استفاده می کنید، باید یک PeriodicWorkRequest جدید ایجاد کنید. در غیر این صورت، باید از OneTimeWorkRequest استفاده کنید.

بیایید ببینیم برنامه ریزی یک Job پیچیده با FirebaseJobDispatcher چگونه ممکن است به نظر برسد:

کاتلین

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)

جاوا

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

برای رسیدن به همان کار با WorkManager باید:

  • ایجاد داده های ورودی که می تواند به عنوان ورودی برای Worker استفاده شود.
  • یک WorkRequest با داده های ورودی و محدودیت هایی مشابه مواردی که در بالا برای FirebaseJobDispatcher تعریف شده است بسازید.
  • WorkRequest در نوبت قرار دهید.

تنظیم ورودی ها برای Worker

FirebaseJobDispatcher از یک Bundle برای ارسال داده های ورودی به JobService استفاده می کند. WorkManager به جای آن Data استفاده می کند. بنابراین تبدیل می شود:

کاتلین

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

جاوا

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

تنظیم محدودیت ها برای کارگر

FirebaseJobDispatcher از Job.Builder.setConstaints(...) برای تنظیم محدودیت‌ها در کارها استفاده می‌کند. WorkManager به جای آن از Constraints استفاده می کند.

کاتلین

import androidx.work.*

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

جاوا

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 (یک بار یا دوره ای)

برای ایجاد OneTimeWorkRequest و PeriodicWorkRequest باید از OneTimeWorkRequest.Builder و PeriodicWorkRequest.Builder استفاده کنید.

برای ایجاد OneTimeWorkRequest که مشابه Job فوق است، باید موارد زیر را انجام دهید:

کاتلین

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()

جاوا

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

تفاوت اصلی در اینجا این است که کارهای WorkManager همیشه در سراسر راه‌اندازی مجدد دستگاه به طور خودکار ادامه دارند.

اگر می‌خواهید یک PeriodicWorkRequest ایجاد کنید، باید کاری مانند:

کاتلین

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()

جاوا

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

برنامه ریزی کار

اکنون که Worker و WorkRequest را تعریف کرده اید، آماده زمان بندی کار هستید.

هر Job که با FirebaseJobDispatcher تعریف می‌شود دارای یک tag بود که برای شناسایی منحصر به فرد یک Job استفاده می‌شد. همچنین راهی را برای برنامه ارائه می‌کند تا به زمان‌بند بگوید که آیا این نمونه از Job قرار است با فراخوانی setReplaceCurrent جایگزین یک کپی موجود از Job شود.

کاتلین

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()

جاوا

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، می‌توانید با استفاده از APIهای enqueueUniqueWork() و enqueueUniquePeriodicWork() (به ترتیب هنگام استفاده از OneTimeWorkRequest و PeriodicWorkRequest به همان نتیجه برسید). برای اطلاعات بیشتر، به صفحات مرجع WorkManager.enqueueUniqueWork() و WorkManager.enqueueUniquePeriodicWork() مراجعه کنید.

این چیزی شبیه به این خواهد بود:

کاتلین

import androidx.work.*

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

جاوا

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

لغو کار

با FirebaseJobDispatcher می توانید کار را با استفاده از:

کاتلین

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

جاوا

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

هنگام استفاده از WorkManager می توانید از موارد زیر استفاده کنید:

کاتلین

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

جاوا

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

راه اندازی WorkManager

WorkManager معمولاً خود را با استفاده از ContentProvider مقداردهی اولیه می کند. اگر به کنترل بیشتری بر نحوه سازماندهی و زمان بندی کار WorkManager نیاز دارید، می توانید پیکربندی WorkManager و مقداردهی اولیه را سفارشی کنید .