نقل البيانات من Firebase JobDispatcher إلى WorkManager

WorkManager عبارة عن مكتبة لجدولة المهام وتنفيذها في الخلفية يمكن تأجيلها في Android. وهذا هو البديل الذي يُنصح به لأداة Firebase JobDispatcher. تشير رسالة الأشكال البيانية سيرشدك الدليل التالي خلال عملية نقل بيانات Firebase تنفيذ JobDispatcher إلى WorkManager.

إعداد Gradle

لاستيراد مكتبة WorkManager إلى مشروع Android، أضِف والتبعيات المدرجة في بدء استخدام WorkManager

من JobService إلى العمال

FirebaseJobDispatcher فئة فرعية من JobService كنقطة دخول لتحديد العمل الذي يجب القيام به. من المحتمل أنك باستخدام JobService مباشرةً أو باستخدام SimpleJobService

سيبدو JobService على النحو التالي:

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، سيتم إلغاء onRunJob()، التي تُرجع النوع @JobResult int.

الفرق الأساسي هو عند استخدام JobService بشكل مباشر، onStartJob(). إلى سلسلة التعليمات الرئيسية، ويتحمّل التطبيق مسؤولية تفريغ البيانات إلا أنه سيتم العمل مع سلسلة محادثات في الخلفية. من ناحية أخرى، إذا كنت تستخدم SimpleJobService، تكون هذه الخدمة مسؤولة عن تنفيذ عملك على سلسلة محادثات في الخلفية.

لدى WorkManager مفاهيم مماثلة. إن وحدة العمل الأساسية في WorkManager هي ListenableWorker تتوفر أيضًا أنواع فرعية مفيدة أخرى من العمال مثل Worker، RxWorker وCoroutineWorker (متى باستخدام كوروتينات Kotlin).

ربط JobService بـ ListenableWorker

إذا كنت تستخدم JobService مباشرةً، يكون العامل الذي يتم تعيينه إليه ListenableWorker إذا كنت تستخدم SimpleJobService، عليك استخدام Worker بدلاً من ذلك.

لنستخدم المثال أعلاه (MyJobService) ونلقي نظرة على كيفية تحويله. إلى ListenableWorker.

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 هي 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 إلى عامل

عند استخدام SimpleJobService، سيظهر العامل أعلاه على النحو التالي:

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

تعرض الدالة doWork() هنا مثيلاً من ListenableWorker.Result للإشارة إلى العمل. لإنجازها بشكل متزامن. هذا الحدث مشابه للوضع "SimpleJobService" الذي يتم فيه تحديد جداول زمنية. وظيفتان في سلسلة محادثات في الخلفية.

ربط JobBuilder بطلب WorkRequest

تستخدم FirebaseJobBuilder Job.Builder لتمثيل بيانات Job الوصفية. مدير العمل يستخدم WorkRequest لشغل هذا الدور.

يتضمّن WorkManager نوعَين من WorkRequest: OneTimeWorkRequest و PeriodicWorkRequest

إذا كنت تستخدم حاليًا Job.Builder.setRecurring(true)، فينبغي عليك إنشاء PeriodicWorkRequest جديد بخلاف ذلك، يجب عليك استخدام OneTimeWorkRequest

لنلقِ نظرة على التأثير المحتمل في جدولة Job مع FirebaseJobDispatcher يشبهون:

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

لتحقيق الأمر باستخدام WorkManager، عليك إجراء ما يلي:

  • يمكنك إنشاء بيانات إدخال يمكن استخدامها كإدخال في Worker.
  • إنشاء WorkRequest باستخدام بيانات الإدخال والقيود المشابهة لتلك المحدد أعلاه لـ FirebaseJobDispatcher.
  • أدرِج WorkRequest في قائمة الانتظار.

إعداد إدخالات للعامل

يستخدم FirebaseJobDispatcher Bundle لإرسال بيانات الإدخال إلى JobService. يستخدم WorkManager Data بدلاً من ذلك. إذًا، يصبح:

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

وضع القيود للعامل

استخدامان (FirebaseJobDispatcher) Job.Builder.setConstaints(...) لوضع قيود على الوظائف. استخدامات WorkManager Constraints بدلاً من ذلك.

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 (مرة واحدة أو دورية)

لإنشاء OneTimeWorkRequest وPeriodicWorkRequest، عليك استخدام OneTimeWorkRequest.Builder وPeriodicWorkRequest.Builder

لإنشاء OneTimeWorkRequest مشابه لما تم ذكره أعلاه، عليك اختيار Job. قم بما يلي:

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

يتمثل الاختلاف الرئيسي هنا في أن مهام WorkManager تستمر دائمًا عبر إعادة تشغيل الجهاز تلقائيًا.

إذا أردت إنشاء PeriodicWorkRequest، يمكنك تنفيذ إجراء مثل:

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

جدولة العمل

الآن بعد أن حددت Worker وWorkRequest، فأنت جاهز لما يلي: جدولة العمل.

كل Job تم تحديده مع FirebaseJobDispatcher كان له tag والذي تم استخدامه من أجل حدِّد بشكلٍ فريد Job. كما قدمت طريقة للتطبيق لإخبار أداة الجدولة إذا كان هذا المثيل من Job سيستبدل نسخة حالية من Job من خلال الاتصال بالرقم setReplaceCurrent.

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، يمكنك تحقيق النتيجة نفسها باستخدام واجهتا برمجة التطبيقات enqueueUniqueWork() وenqueueUniquePeriodicWork() (عند استخدام OneTimeWorkRequest وPeriodicWorkRequest على التوالي). لمزيد من المعلومات، المعلومات، فراجع الصفحات المرجعية WorkManager.enqueueUniqueWork() وWorkManager.enqueueUniquePeriodicWork()

سيبدو هذا مشابهًا لما يلي:

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

جارٍ إلغاء العمل

من خلال FirebaseJobDispatcher، يمكنك إلغاء العمل باستخدام:

Kotlin

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

Java

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

عند استخدام WorkManager، يمكنك استخدام:

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

عادةً ما يقوم WorkManager بإعداد نفسه باستخدام ContentProvider. إذا كنت بحاجة إلى مزيد من التحكم في كيفية تنظيم تطبيق WorkManager وجداوله الزمنية، يمكنك تخصيص ضبط WorkManager وتهيئته.