WorkManager跟JobScheduler一样用来实现特定情况下的后台服务,但WorkManager功能更加丰富,使用起来也更加简单。
1.WorkManager 简要价绍(来自官网)
使用 WorkManager API 可以轻松地调度即使在应用退出或设备重启时仍应运行的可延迟异步任务。
主要功能:
最高向后兼容到 API 14
在运行 API 23 及以上级别的设备上使用 JobScheduler
在运行 API 14-22 的设备上结合使用 BroadcastReceiver 和 AlarmManager
添加网络可用性或充电状态等工作约束
调度一次性或周期性异步任务
监控和管理计划任务
将任务链接起来
确保任务执行,即使应用或设备重启也同样执行任务
遵循低电耗模式等省电功能
WorkManager 旨在用于可延迟运行(即不需要立即运行)并且在应用退出或设备重启时必须能够可靠运行的任务。例如:
向后端服务发送日志或分析数据
定期将应用数据与服务器同步
WorkManager 不适用于应用进程结束时能够安全终止的运行中后台工作,也不适用于需要立即执行的任务
- 定义Worker
WorkManager 的串并行机制,使我觉得它很适合图像处理的一些计算。举个例子说,计算如下一个值:
Sum = 1 + 2 + … + N
Sum2 = 11 + 22 + …+ N*N
Average = (Sum + Sum2) /N
就可以定义三个Worker,然后使用WorkManager 的串并行机制来完成整个任务。
首先定义Worker:
package net.wen.sample;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class SumWorker extends Worker {
public SumWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
int N = getInputData().getInt(Constants.COUNT, 0);
float total = 0;
for (int i = 0; i < N; ++i){
total += (i+1);
}
Log.d("wenpd", "sum1=" + total);
Data outData = new Data.Builder().putFloat(Constants.SUM, total).build();
return Result.success(outData);
}
}
SumWorker 2类似,然后:
package net.wen.sample;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class AverageWorker extends Worker {
public AverageWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
int N = getInputData().getInt(Constants.COUNT, 0);
if(N < 2) {
return Result.failure();
}
float sum1 = getInputData().getFloat(Constants.SUM, 0);
float sum2 = getInputData().getFloat(Constants.SUM2, 0);
Log.d("wenpd", "sum1=" + sum1 + ";sum2=" + sum2);
float ave = (sum1 + sum2 ) / N;
Log.d("wenpd", "ave=" + ave);
Data data = new Data.Builder().putFloat(Constants.AVERAGE, ave).build();
return Result.success(data);
}
}
- Request定义与使用
package net.wen.sample;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import android.os.Bundle;
import com.google.android.material.snackbar.Snackbar;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doWorks(100);
}
private void doWorks(int n) {
Constraints constraints = new Constraints.Builder()
//.setRequiresCharging(true)
//.setRequiresDeviceIdle(true)
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)
.build();
Data data = new Data.Builder().putInt(Constants.COUNT, n).build();
OneTimeWorkRequest sumRequest = new OneTimeWorkRequest.Builder(SumWorker.class)
.setInitialDelay(10, TimeUnit.MILLISECONDS)
.setConstraints(constraints)
.setInputData(data)
.build();
OneTimeWorkRequest sum2Request = new OneTimeWorkRequest.Builder(SumWorker2.class)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.addTag("sum2")
.setInputData(data)
.build();
OneTimeWorkRequest aveRequest = new OneTimeWorkRequest.Builder(AverageWorker.class)
.setInputData(data)
.build();
WorkManager.getInstance(this)
.beginWith(Arrays.asList(sumRequest, sum2Request))
.then(aveRequest)
.enqueue();
WorkManager.getInstance(this).getWorkInfoByIdLiveData(aveRequest.getId())
.observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
float ave = workInfo.getOutputData().getFloat(Constants.AVERAGE, 0);
displayMessage("Work finished! average=" + ave);
}
}
});
}
private void displayMessage(String message) {
Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG).show();;
}
}
- 详细说明
1)输入输出都是通过Data,使用方法跟intent类似:
Data data = new Data.Builder().putFloat(Constants.AVERAGE, ave).build();
setInputData(data)/Result.success(data)
WorkManager.getInstance(this)
.beginWith(Arrays.asList(sumRequest, sum2Request))
.then(aveRequest)
.enqueue();
WorkManager的串行执行,会将sumRequest, sum2Request的输出当作输入传递给aveRequest。管理来自多个父级 OneTimeWorkRequest 的输入,WorkManager 使用 InputMerger,具体介绍请参考管网:
链接和工作状态
2)获取工作结果(叫 观察工作状态 比较全面)
WorkManager通过livedata来观察工作状态,例如获取结果,显示进度等, 即代码中的:
WorkManager.getInstance(this).getWorkInfoByIdLiveData(aveRequest.getId())
.observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
float ave = workInfo.getOutputData().getFloat(Constants.AVERAGE, 0);
displayMessage("Work finished! average=" + ave);
}
}
});
显示进度,可参考官网的观察工作器的中间进度
6. 其他
除了OneTimeWorkRequest, 还有PeriodicWorkRequest用来执行周期性任务,周期性任务最小间隔是15分钟,而Worker最长执行时间是20分钟,超过就会返回fail。关于Constraints 与PeriodicWorkRequest,以及WorkManager其它高级部分功能,官网都有详细介绍。