WorkManager کتابخانهای برای زمانبندی و اجرای کارهای پسزمینهای قابلتعویق در Android است. این ابزار جایگزین توصیهشده برای 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 وجود دارد، مثل
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 را برمیگرداند،
که برای علامتدهی تکمیل کار بهصورت ناهمگام استفاده میشود. باید استراتژی رشتهبندی خودتان را در اینجا انتخاب کنید.
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();
تنظیم محدودیتها برای Worker
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 قرار است جایگزین نسخه موجود Job شود یا نه، با فراخوانی setReplaceCurrent.
کاتلین
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، میتوانید بااستفاده از میاناهای برنامهسازی کاربردی
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 را سفارشیسازی کنید.