מעבר מ-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() נקראת ב-thread הראשי, ובאחריות האפליקציה להסיר פועלים לשרשור ברקע. מצד שני, אם אתם משתמשים 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 ל-Worker

כשמשתמשים ב-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 לתור.

הגדרת מקורות קלט ל-Worker

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

הגדרת מגבלות ל-Worker

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

יצירת בקשת העבודה (חד-פעמית או תקופתית)

כדי ליצור 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, אפשר להשיג את אותה תוצאה באמצעות ממשקי API של 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.