WorkManager系列(十六)Migrating from Firebase JobDispatcher to WorkManager

这一篇是讲如何从Firebase JobDispatcher 迁移到 WorkManager,其实Firebase JobDispatcher 因为需要google play service,所以国内用不了,这也也就没有需要迁移的问题

不管怎么样,这是WorkManager的最后一篇,还是贴出来吧

----------------------------------------------------------

WorkManger从18年五月推出,到现在也就一年多。

WorkManager is a library for scheduling and executing deferrable background work in Android. It is the recommended replacement for Firebase JobDispatcher. The following guide will walk you through the process of migrating your Firebase JobDispatcher implementation to WorkManager.

Gradle setup

Note: The first step in migrating away from Firebase JobDispatcher is to include WorkManager’s latest gradle dependencies.

To import WorkManager into your Android project, see the instructions for declaring dependencies in the WorkManager release notes.

From JobService to Workers

FirebaseJobDispatcher uses a subclass of JobService as an entry point for defining the work which needs to be done. You might be using JobService directly, or using SimpleJobService.

JobService will look something like this:

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

If you are using SimpleJobService you will have overridden onRunJob(), which returns a @JobResult int type.

The key difference is when you are using JobService directly, onStartJob() is called on the main thread, and it is the app’s responsibility to offload the work to a background thread. On the other hand, if you are using SimpleJobService, that service is responsible for executing your work on a background thread.

WorkManager has similar concepts. The fundamental unit of work in WorkManager is a ListenableWorker. There are also other useful subtypes of workers like WorkerRxWorker, and CoroutineWorker (when using Kotlin coroutines).

JobService maps to a ListenableWorker

If you are using JobService directly, then the worker it maps to is a ListenableWorker. If you are using SimpleJobService then you should use Worker instead.

Let’s use the above example (MyJobService) and look at how we can convert it to a 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 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.
  }
}

The basic unit of work in WorkManager is a ListenableWorker. Just like JobService.onStartJob()startWork()is called on the main thread. Here MyWorker implements ListenableWorker and returns an instance ofListenableFuture, which is used to signal work completion asynchronously. You should choose your own threading strategy here.

The ListenableFuture here eventually returns a ListenableWorker.Result type which can be one of Result.success()Result.success(Data outputData)Result.retry()Result.failure(), or Result.failure(Data outputData). For more information, please see the reference page forListenableWorker.Result.

onStopped() is called to signal that the ListenableWorker needs to stop, either because the constraints are no longer being met (for example, because the network is no longer available), or because a WorkManager.cancel…()method was called. onStopped() may also be called if the OS decides to shut down your work for some reason.

SimpleJobService maps to a Worker

When using SimpleJobService the above worker will look like:

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

Here doWork() returns an instance of ListenableWorker.Result to signal work completion synchronously. This is similar to SimpleJobService, which schedules jobs on a background thread.

JobBuilder maps to WorkRequests

FirebaseJobBuilder uses Job.Builder to represent Job metadata. WorkManager uses WorkRequest to fill this role.

WorkManager has two types of WorkRequests: OneTimeWorkRequest and PeriodicWorkRequest.

If you are currently using Job.Builder.setRecurring(true), then you should create a new PeriodicWorkRequest. Otherwise, you should use a OneTimeWorkRequest.

Let’s look at what scheduling a complex Job with FirebaseJobDispatcher might look like:

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

To achieve the same with WorkManager you will need to:

  • Build input data which can be used as input for the Worker.
  • Build a WorkRequest with the input data and constraints similar to the ones defined above for FirebaseJobDispatcher.
  • Enqueue the WorkRequest.

Setting up inputs for the Worker

FirebaseJobDispatcher uses a Bundle to send input data to the JobService. WorkManager uses Data instead. So that becomes:

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

Setting up Constraints for the Worker

FirebaseJobDispatcher uses Job.Builder.setConstaints(...) to set up constraints on jobs. WorkManager usesConstraints instead.

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

Creating the WorkRequest (OneTime or Periodic)

To create OneTimeWorkRequests and PeriodicWorkRequests you should use OneTimeWorkRequest.Builder and PeriodicWorkRequest.Builder.

To create a OneTimeWorkRequest which is similar to the above Job you should do the following:

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

The key difference here is that WorkManager’s jobs are always persisted across device reboot automatically.

If you want to create a PeriodicWorkRequest then you would do something like:

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

Scheduling work

Now that you have defined a Worker and a WorkRequest, you are ready to schedule work.

Every Job defined with FirebaseJobDispatcher had a tag which was used to uniquely identify a Job. It also provided a way for the application to tell the scheduler if this instance of a Job was to replace an existing copy of theJob by calling setReplaceCurrent.

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

When using WorkManager, you can achieve the same result by using enqueueUniqueWork() and enqueueUniquePeriodicWork() APIs (when using a OneTimeWorkRequest and a PeriodicWorkRequest, respectively). For more information, see the reference pages for WorkManager.enqueueUniqueWork() and WorkManager.enqueueUniquePeriodicWork().

This will look something like:

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

OneTimeWorkRequest workRequest = // a WorkRequest;
WorkManager.getInstance()
    // 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);

Note: Job tags in FirebaseJobDispatcher map to names of WorkRequests for WorkManager.

Cancelling work

With FirebaseJobDispatcher you could cancel work using:

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

When using WorkManager you can use:

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

Initializing WorkManager

WorkManager needs to be initialized once per app, typically using a ContentProvider or an Application.onCreate().

WorkManager typically initializes itself by using a ContentProvider. However, there are some subtle differences in defaults with regard to the size of the threadpool, and the number of workers that can be scheduled at a given time. So you might need to customize WorkManager.

Typically, this customization is done using WorkManager.initialize(). This allows you to customize the background Executor used to run Workers, and the WorkerFactory used to construct Workers. (WorkerFactory is useful in the context of dependency injection). Please read the documentation for this method to make sure you stop automatic initialization of WorkManager.

For more information, see the documentation for initialize() and for Configuration.Builder.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值