1.简介
Android上有许多可延期的后台工作选项。此代码实验室涵盖WorkManager,这是一个可延迟的后台工作的兼容,灵活且简单的库。WorkManager是Android上推荐的任务调度框架,用于可延缓的工作,并且可以执行。
1.1 什么是WorkManager
WorkManager是Android Jetpack的一部分,是用于后台工作的架构组件,需要兼顾机会和有保证的执行。机会性执行意味着WorkManager将尽快完成您的后台工作。有保证的执行意味着即使在离开应用程序的情况下,WorkManager也会兼顾各种情况下开始逻辑工作。
WorkManager是一个简单但非常灵活的库,它具有许多其他优点。这些包括:
- 支持异步一次性和定期任务
- 支持网络条件,存储空间和充电状态等约束
- 链接复杂的工作请求,包括并行运行工作
- 一个工作请求的输出用作下一个工作的输入
- 将API级别的兼容性处理回API级别14(请参阅注释)
- 可以使用或不使用Google Play服务
- 遵循系统健康最佳实践
- LiveData支持可轻松在UI中显示工作请求状态
1.2 何时使用WorkManager
即使用户离开特定屏幕或您的应用程序,WorkManager库对于完成有用的任务也是不错的选择。
可以很好地使用WorkManager的一些任务示例:
- 上载日志
- 将过滤器应用于图像并保存图像
- 定期将本地数据与网络同步
WorkManager提供有保证的执行,但并非所有任务都需要执行。因此,这不是将所有任务从主线程中运行掉的万能方法。有关何时使用WorkManager的更多详细信息,请参阅《后台处理指南》。
$ git clone -b java https://github.com/googlecodelabs/android-workmanager
2.将WorkManager添加到您的应用程序
WorkManager
需要下面的gradle依赖项。这些已包含在构建文件中:
app / build.gradle
dependencies {
// Other dependencies
implementation "androidx.work:work-runtime:$versions.work"
}
您应该work-runtime
从此处获取最新版本,然后输入正确的版本。目前,最新版本为:
build.gradle
versions.work = "2.3.3"
如果您将版本更新为较新的版本,请确保立即同步以将项目与更改的gradle文件同步。
2.1 WorkManager基础
您需要了解一些WorkManager类:
- Worker:在此处将要执行的实际工作的代码放在后台。您将扩展此类并重写doWork()方法。
- WorkRequest:这表示需要做一些工作。您将在Worker创建的过程中将自己的信息传递给WorkRequest。制作时,WorkRequest您还可以指定类似Constraints何时Worker运行的内容。
-
WorkManager:此类实际上是安排您的
WorkRequest
并使其运行。它以分散系统资源负载的方式调度WorkRequest
,同时遵守您指定的约束。
2.2 创建步骤
第一步:创建BlurWorker 扩展自Worker
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.Log;
import com.example.background.R;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
public class BlurWorker extends Worker {
public BlurWorker(
@NonNull Context appContext,
@NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
}
private static final String TAG = BlurWorker.class.getSimpleName();
@NonNull
@Override
public Result doWork() {
Context applicationContext = getApplicationContext();
try {
Bitmap picture = BitmapFactory.decodeResource(
applicationContext.getResources(),
R.drawable.test);
// Blur the bitmap
Bitmap output = WorkerUtils.blurBitmap(picture, applicationContext);
// Write bitmap to a temp file
Uri outputUri = WorkerUtils.writeBitmapToFile(applicationContext, output);
WorkerUtils.makeStatusNotification("Output is "
+ outputUri.toString(), applicationContext);
// If there were no errors, return SUCCESS
return Result.success();
} catch (Throwable throwable) {
// Technically WorkManager will return Result.failure()
// but it's best to be explicit about it.
// Thus if there were errors, we're return FAILURE
Log.e(TAG, "Error applying blur", throwable);
return Result.failure();
}
}
}
第二步:在ViewModel中获取WorkManager
在中为WorkManager
实例创建变量,ViewModel
并在ViewModel
的构造函数中实例化该变量:
private WorkManager mWorkManager;
// BlurViewModel constructor
public BlurViewModel(@NonNull Application application) {
super(application);
mWorkManager = WorkManager.getInstance(application);
//...rest of the constructor
}
第三步:在WorkManager中排队工作请求
好了,是时候发出一个WorkRequest并告诉WorkManager运行它了。WorkRequest
s 有两种类型:
-
OneTimeWorkRequest:
WorkRequest
只会执行一次的A。 -
PeriodicWorkRequest:
WorkRequest
一个周期将重复的A。
我们只希望单击“ 转到”按钮时使图像模糊一次。单击“ applyBlur
执行”按钮时将调用该方法,因此请OneTimeWorkRequest
从BlurWorker
此处创建一个。然后,使用您的WorkManager
实例将您的WorkRequest.
将以下代码行添加到BlurViewModel
的applyBlur()方法中:
BlurViewModel.java
void applyBlur(int blurLevel) {
mWorkManager.enqueue(OneTimeWorkRequest.from(BlurWorker.class));
}
第四步:添加输入和输出
- 第1步-创建数据输入对象
- 将数据对象传递给WorkRequest
- 更新BlurWorker的doWork()以获取输入
- 输出临时URI
输入和输出通过Data对象传入和传出。Data
对象是键/值对的轻量级容器。他们是为了存储小数据量,可能通过WorkRequest输入
和输出。
/**
* 创建包含Uri的输入数据
* @return Data类,包含Uri的字符串
*/
private Data createInputDataForUri() {
Data.Builder builder = new Data.Builder();
if (mImageUri != null) {
builder.putString(KEY_IMAGE_URI, mImageUri.toString());
}
return builder.build();
}
void applyBlur(int blurLevel) {
OneTimeWorkRequest blurRequest =
new OneTimeWorkRequest.Builder(BlurWorker.class)
.setInputData(createInputDataForUri())
.build();
mWorkManager.enqueue(blurRequest);
}
更新BlurWorker
的doWork()
方法,以获取从Data
对象传递来的URI :
public Result doWork() {
Context applicationContext = getApplicationContext();
// ADD THIS LINE
String resourceUri = getInputData().getString(Constants.KEY_IMAGE_URI);
//... rest of doWork()
}
我们已经完成了此Worker的工作,现在可以返回Result.success()
。我们将提供OutputURI作为输出数据。
Data outputData = new Data.Builder()
.putString(KEY_IMAGE_URI, outputUri.toString())
.build();
return Result.success(outputData);
3.链式工作
WorkManager允许您创建独立的WorkerRequest
按顺序或并行运行。在这一步中,您将创建一个如下所示的工作链:
一个链接的输出WorkRequest
成为链接中下一链接的输入WorkRequest
。各个WorkRequest
彼此之间传递的输入和输出,并在上图中显示为蓝色部分的文本。
现在不是通过WorkManager.enqueue()来发送请求,而是调用WorkManager.beginWith(),它放回WorkContinuation类,用来定义工作请求链。
// Example code. Don't copy to the project
WorkContinuation continuation = mWorkManager.beginWith(workA);
continuation.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue(); // Enqueues the WorkContinuation which is a chain of work
4.确保单独的工作
如果您一次只希望运行一个工作链,您可以使用beginUniqueWork代替beginWith;
确保模糊文件的工作链是唯一的beginUniqueWork。传递IMAGE_MANIPULATION_WORK_NAME作为密钥。您还需要传递ExistingWorkPolicy。你的选择是REPLACE,KEEP或者APPEND。
WorkContinuation continuation = mWorkManager
.beginUniqueWork(IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker.class));
5.添加请求标签和显示工作状态
您将使用标签来标记您的工作,而不是使用WorkManager ID
OneTimeWorkRequest save = new OneTimeWorkRequest.Builder(SaveImageToFileWorker.class)
.addTag(TAG_OUTPUT) // This adds the tag
.build();
5.1 获取WorkInfo
// New instance variable for the WorkInfo class
private LiveData<List<WorkInfo>> mSavedWorkInfo;
// Placed this code in the BlurViewModel constructor
mSavedWorkInfo = mWorkManager.getWorkInfosByTagLiveData(TAG_OUTPUT);
// Add a getter method for mSavedWorkInfo
LiveData<List<WorkInfo>> getOutputWorkInfo() { return mSavedWorkInfo; }
5.2 显示WorkInfo
使用以下workInfo.getState().isFinished()
检查工作状态是否已完成 ;
// Show work status, added in onCreate()
mViewModel.getOutputWorkInfo().observe(this, listOfWorkInfos -> {
// If there are no matching work info, do nothing
if (listOfWorkInfos == null || listOfWorkInfos.isEmpty()) {
return;
}
// We only care about the first output status.
// Every continuation has only one worker tagged TAG_OUTPUT
WorkInfo workInfo = listOfWorkInfos.get(0);
boolean finished = workInfo.getState().isFinished();
if (!finished) {
showWorkInProgress();
} else {
showWorkFinished();
}
});
6.取消工作
您可以使用ID,标签和唯一链名取消工作。
void cancelWork() {
mWorkManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME);
}