欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android WorkerManager 的使用

程序员文章站 2022-03-30 18:45:03
简介WorkerManager 适用于执行可以延迟(不需要精确时间)但是必须要稳定执行的的后台任务。适用于向后台同步应用数据,发送日志,应用检查更新等不需要及时完成的后台任务。在本文中这类型的后台任务,命名为延迟后台任务,方便理解。在Android8.0以后Android系对后台的服务有了严格的限制,因此执行后台任务,需要通过系统调度的方式来执行,Google官方推荐使用JobScheduler 作业替换后台 Service。但是WorkerManager 是JetPack中的一个组成部分,并且完成能够...

简介

WorkerManager 适用于执行可以延迟(不需要精确时间)但是必须要稳定执行的的后台任务。适用于向后台同步应用数据,发送日志,应用检查更新等不需要及时完成的后台任务。在本文中这类型的后台任务,命名为延迟后台任务,方便理解。
在Android8.0以后Android系对后台的服务有了严格的限制,因此执行后台任务,需要通过系统调度的方式来执行,Google官方推荐使用JobScheduler 作业替换后台 Service。但是WorkerManager 是JetPack中的一个组成部分,并且完成能够胜任JobScheduler,并且有比JobScheduler 更加好用,因此推荐使用WorkerManager。文章除了介绍WorkerManager的用法,也会将WorkerManager 与 JobScheduler进行对比。

简要使用

后台延迟任务可以分为一次性任务和周期性任务。一次性任务,并不是只执行一次,如果任务没有执行成功,可以进行重试策略,重试策略WorkerManager 和 JobScheduler 的限制条件是一样的,最小的重试时间为10S,最大的重试时间为5小时。

一次性任务

WorkerManager 一次性任务

  1. 继承Worker。
public class MyWork extends Worker {
    private final String TAG = "SecondWork";
    public SecondWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG,"thread id = "+Thread.currentThread().getId());
        Log.d(TAG,"执行时间  :"+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return Result.success();
    }
}
  1. 创建WorkerManager,并且将自定义的Worker添加到系统中,等待系统的调度
        OneTimeWorkRequest secondWork = new OneTimeWorkRequest.Builder(MyWork .class)
                .setInitialDelay(5000,TimeUnit.MILLISECONDS) // 在满足约束条件的前提下,初始延迟时间为5S
                .setBackoffCriteria(BackoffPolicy.LINEAR,10,TimeUnit.SECONDS) // 重试间隔时间为:curTime + 10 * 重试次数
                .build();
        WorkManager.getInstance(getContext()).enqueue(secondWork);
        Log.d(TAG,"SecondWork 添加到系统  "+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));

JobScheduler 一次性任务

  1. 继承JobService。
public class MyService extends JobService{
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG,"ThirdService onStartJob="+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return false;
    }
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(TAG,"ThirdService onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return false;
    }
}
  1. 获取系统调度jobscheduler,将自定义的JobService,添加到系统中,开始系统调度。
JobScheduler scheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName jobService = new ComponentName(getContext(), ThirdService.class);
// 注意jobId
JobInfo.Builder builder = new JobInfo.Builder(1000, jobService);
JobInfo jobInfo = builder
                .setMinimumLatency(3*1000)
                .setOverrideDeadline(4*1000)
                .build();
 scheduler.schedule(jobInfo);     

周期性任务

WorkerManager 周期性任务

  1. 继承Worker,这个一步跟一次性任务是一样的。
public class MyWork extends Worker {
    private final String TAG = "SecondWork";
    public SecondWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG,"thread id = "+Thread.currentThread().getId());
        Log.d(TAG,"执行时间  :"+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return Result.success();
    }
}
  1. 创建WorkerManager,并且将自定义的Worker添加到系统中,等待系统的调度。
PeriodicWorkRequest thirdWork = new PeriodicWorkRequest.Builder(SecondWork.class,
                15,TimeUnit.MINUTES) // 周期性任务的间隔时间最小为15分钟
                .build();
        WorkManager.getInstance(getContext()).enqueue(thirdWork);
        Log.d(TAG,"thirdWork 添加到系统  "+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));

注意,repeatInterval 和 flexInterval 的区别。

JobScheduler 周期性任务

  1. 继承JobService。
public class MyService extends JobService{
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG,"ThirdService onStartJob="+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return false;
    }
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(TAG,"ThirdService onStopJob = "+DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        return false;
    }
}
  1. 获取系统调度jobscheduler,将自定义的JobService,添加到系统中,开始系统调度。
JobScheduler scheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName jobService = new ComponentName(getContext(), ThirdService.class);
// 注意jobId
JobInfo.Builder builder = new JobInfo.Builder(1000, jobService);
JobInfo jobInfo = builder
                .setPeriodic(3*1000)
                .build();
 scheduler.schedule(jobInfo);     

不同点

  1. 使用WorkerManager 的语义话程度比JobScheduler 语义程度高,WorkerManager有明确的区分一次性任务还是周期性任务,JobScheduler 只能通过约束条件设置,任务最大的延迟来,执行一次性任务。周期性任务与和非周期性任务是互斥的,在WorkerManager 中通过两个明确的类将两者进行区别,在JobScheduler 是通过设置约束条件时抛出异常来体现,因此WorkerManager 的易用性更强。
  2. 自定义Worker 中的doWork 是在线程中的,可以执行耗时操作。自定义的JobService 中的onStartJob,onStopJob在UI线程,不能执行耗时任务。
  3. WorkerManager的时间约束条件setInitialDelay,含义是在满足约束条件的前提下,初始延迟时间,如果没有满足约束条件会一直等到约束条件满足,再次判断是否满足时间约束条件,如果满足约束条件则只需Worker。JobScheduler 的时间约束条件setOverrideDeadline的含义是到了最大延迟时间后,即使不满足约束条件也会只需JobService。
  4. WorkerManager 中的自定义Worker返回值有着明确的含义,成功Result.success();失败Result.failure();重试Result.retry();JobScheduler 中的JobService 需要通过jobFinished来确定,是否需要重试,jobFinished中第二个参数为true,需要重试,否则不需要重试。
  5. WorkerManager 和 JobScheduler的兼容性不同,WorkerManager兼容到Android14,但是JobScheduler 最低的版本要求是21。因此WorkerManager的兼容范围更广。

JobId

  1. 在JobScheduler 中构建JobInfo 需要指定JobId,并且这个JobId必须是系统唯一的。这一个要求想要达到是有很大的难度的,在应用中使用代码很难获取到其他应用创建的JobId,因此很难保证JobId的唯一性。同时如果JobId不唯一,在Android 7.0 的某些机型测试时发现会造成调度时间的混乱,这个可能会被其人利用,这时候出现的错误是很难发现和排查的。
  2. 在WorkerManager 中取消了JobId的概念,取而代之的是使用UUID,并且UUID是系统来生成,这个做的好处是可以最大程度的规避JobScheduler 遇到的问题。是一个可喜的变化。

WorkerManager 详细使用

下面记录WorkerManager的使用方式。

Worker状态

使用WorkerManager中最为重要的一步是继承Worker,其中Worker是具有状态,并且状态可以监控的,因此可以根据Worker的状态灵活对其进行控制。
Worker状态其实和线程的状态非常像,Worker状态如下:

  • ENQUEUED 排队(就绪)状态,这时候自定义的Worker已进入队列,在等待约束条件满足。
  • RUNNING 运行状态。
  • SUCCEEDED 成功结束状态
  • FAILED 失败结束状态
  • BLOCKED 阻塞状态
  • CANCELLED 取消状态

监听Worker状态源码,在必要的情况下面可以根据不同的状态获取数据,进行自定义的数据变化如下:

WorkManager.getInstance(getContext()).getWorkInfoByIdLiveData(firstWork.getId())
                .observe(getViewLifecycleOwner(), new Observer<WorkInfo>() {
                    @Override
                    public void onChanged(WorkInfo workInfo) {
                        if (workInfo != null){
                            switch (workInfo.getState()){
                                case RUNNING:
                                    Log.d(TAG,"运行状态");
                                    break;
                                case SUCCEEDED:
                                    Log.d(TAG,"成功");
                                    break;
                                    ...
                                default:
                                    break;
                            }
                        }
                    }
                });

构建任务

构建一次性任务参数

构建一次性任务参数源码如下

OneTimeWorkRequest firstWork = new OneTimeWorkRequest.Builder(FirstWork.class)
                .setInitialDelay(3000, TimeUnit.MILLISECONDS)// 设置任务初始延时
                .setConstraints(new Constraints.Builder().build())// 设置约束条件
                //.setInputMerger(new InputMerger()) //在工作链的时候使用
                .setInputData(new Data.Builder().build()) // 设置输入数据
                .setBackoffCriteria(BackoffPolicy.LINEAR, 11, TimeUnit.SECONDS)// 设置重试策略
                .addTag("first_work") // 添加tag,方便使用
                .keepResultsForAtLeast(10,TimeUnit.SECONDS) // Worker 运行结果保留时间
                //.setInitialRunAttemptCount() 设置任务运行的次数(测试重试策略的时候使用),测试使用
                //.setInitialState()// 设置Worker的状态,也是测试使用,应用不能调用
                //.setScheduleRequestedAt() // android 做测试使用,应用是一般不调用
                //.setPeriodStartTime(3000, TimeUnit.MILLISECONDS)// android 做测试使用,应用是一般不调用
                .build();

构建周期性任务参数

构建周期性任务参数如下

PeriodicWorkRequest thirdWork = new PeriodicWorkRequest.Builder(SecondWork.class,
                    15, TimeUnit.MINUTES, 15, TimeUnit.MINUTES)
                    .setInputData(new Data.Builder().putInt("number",i).build())
                    //.setInitialDelay(10,TimeUnit.SECONDS)// 周期性任务的初始延迟时间,不设置,在满足条件后立即执行.
                    .setConstraints(new Constraints.Builder().build())// 设置约束条件
                    //.setInputMerger(new InputMerger()) //在工作链的时候使用
                    .setInputData(new Data.Builder().build()) // 设置输入数据
                    .setBackoffCriteria(BackoffPolicy.LINEAR, 11, TimeUnit.SECONDS)// 设置重试策略
                    .addTag("first_work") // 添加tag,方便使用
                    .keepResultsForAtLeast(10,TimeUnit.SECONDS) // Worker 运行结果保留时间
                    .build();

比较一次性任务和周期性任务约束参数

由此可见,构建一次性任务参数和周期性任务参数处理Builder传入的参数不同,其他的约束参数是相同的。这也跟 OneTimeWorkRequestPeriodicWorkRequest都继承与WorkRequest相关。相同的约束参数都是封装在WorkRequest,特殊的参数封装在子类中。后续有自定义的任务可以继承WorkRequest,实现自定义化。

构建约束条件

在JobScheduler的约束条件在构建JobInfo中设置,在WorkerManager中有单独的设置类Constraints,可配置的约束条件如下

Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .setRequiresCharging(true)
                .setRequiresStorageNotLow(true)
                .setRequiresDeviceIdle(true)
                .setRequiresBatteryNotLow(true)
                //.addContentUriTrigger(new Uri(),false)
                .build()

任务具有可管理性

WorkerManager 比 JobScheduler 更为友好的一方面是可以更好管理Worker。WokerManager通过以下接口,对Worker进行管理

public abstract @NonNull LiveData<WorkInfo> getWorkInfoByIdLiveData(@NonNull UUID id);
public abstract @NonNull ListenableFuture<WorkInfo> getWorkInfoById(@NonNull UUID id);
public abstract @NonNull LiveData<List<WorkInfo>> getWorkInfosByTagLiveData(@NonNull String tag);
public abstract @NonNull ListenableFuture<List<WorkInfo>> getWorkInfosByTag( @NonNull String tag);
...

观察任务的状态

WorkerManger支持对任务状态的监控,使用代码如下

WorkManager.getInstance(getContext()).getWorkInfoByIdLiveData(firstWork.getId())
                .observe(getViewLifecycleOwner(), new Observer<WorkInfo>() {
                    @Override
                    public void onChanged(WorkInfo workInfo) {
                        if (workInfo != null) {
                            switch (workInfo.getState()) {
                                case RUNNING:
                                    Log.d(TAG, "进度 = " + workInfo.getProgress().getInt("progress", 0));
                                    break;
                                case SUCCEEDED:
                                    Log.d(TAG, "成功");
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                });

任务的参数

WorkerManager可以方便的在调用者,和Woker之间传递参数。示例如下

  1. Woker可以向WorkerManager传递运行过程中的参数和运行结果的参数。
public class FirstWork extends Worker{
public Result doWork() {
        Log.d(TAG,"thread id = "+Thread.currentThread().getId());
        Log.d(TAG,"执行时间  :"+ DateUtil.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        String number = getInputData().getString("number");
        // 向调用者(WorkManager)传递中间过程参数,
	    setProgressAsync(new Data.Builder().putInt("progress", 100).build());
        int aa = Integer.parseInt(number);
        // 构建返回值
        Data outputData = new Data.Builder()
                .putString("number",String.valueOf(aa))
                .build();
        // 向调用者(WorkManager)传递执行结果。      
        return Result.success(outputData);
    }
}
  1. 在调用Woker时,可以传递运行时需要的参数,以及监听Woker回传的参数。示例代码如下
Data data = new Data.Builder()
                .putString("number", "1")
                .build();
 OneTimeWorkRequest firstWork = new OneTimeWorkRequest.Builder(FirstWork.class)
                .setInitialDelay(3000, TimeUnit.MILLISECONDS)
                .setInputData(data)// 传入Worker运行时需要的参数。
                .setBackoffCriteria(BackoffPolicy.LINEAR, 11, TimeUnit.SECONDS)
                .addTag("first_work")
                .build();

监听Worker回传的参数

WorkManager.getInstance(getContext()).getWorkInfoByIdLiveData(firstWork.getId())
                .observe(getViewLifecycleOwner(), new Observer<WorkInfo>() {
                    @Override
                    public void onChanged(WorkInfo workInfo) {
                        if (workInfo != null) {
                            switch (workInfo.getState()) {
                                case RUNNING:
                                    Log.d(TAG, "进度 = " + workInfo.getProgress().getInt("progress", 0));
                                    break;
                                case SUCCEEDED:
                                    Log.d(TAG, "成功");
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                });

任务的取消

取消worker代码如下

WorkManager.getInstance(getContext()).cancelAllWork()
WorkManager.getInstance(getContext()).cancelWorkById()
WorkManager.getInstance(getContext()).cancelAllWorkByTag()

本文地址:https://blog.csdn.net/cxmfzu/article/details/108174154

相关标签: android