一、简介
Android有多个选项用于处理可延迟的后台工作。WorkManager
是一种具有向后兼容性且简单灵活的库,用于处理可延迟的后台工作。WorkManager是Android平台上推荐用于处理可延迟工作的任务调度程序,能够保证工作得到执行。
1.1、什么是WorkManager
WorkManager属于Android Jetpack
的一部分,是一种架构组件,用于处理既需要机会性执行,又需要有保证的执行的后台工作。机会性执行意味着WorkManager会尽快执行您的后台工作。有保证的执行意味着WorkManager会负责通过逻辑障碍在各种情况下启动您的工作,即使用户离开您的应用也无妨。
WorkManager是一个极其灵活的库,具有许多其他优势。这其中包括:
- 支持异步一次性任务和定期任务
- 支持网络条件、存储空间和充电状态等约束条件
- 链接复杂的工作请求,包括并行运行工作
- 将来自一个工作请求的输出用作下一个工作请求的输入
- 处理到API级别的兼容性,可向后兼容至API级别14
- 无论是否使用Google Play服务都可以运行
- 遵循系统健康最佳做法
- 提供LiveData支持,可在界面轻松显示工作请求状态
1.2、何时使用WorkManager
有些任务,即便用户离开特定屏幕或您的应用,也需要完成。对于这些任务,WorkManager库是不错的选择。
以下是一些适合应用Work的任务的典型示例:
- 上传日志
- 对图片应用滤镜并保存图片
- 定期将本地数据与网络同步
WorkManager提供有保证的执行,然而并非所有任务都需要这种保证。因此,它并非运行所有非主线程任务的万全之选。
1.3、后台处理
1.3.1、后台任务的类别
后台任务分为以下几个主要类别:
- 即时任务
- 延期任务
- 精确任务
如需对任务进行分类,请回答以下问题。
任务是否需要在用户与应用进行互动时完成?
如果是,则应将任务归类为即时任务。如果不是,请继续回答第二个问题。
任务是否需要在精确的时间点运行?
如果您确定需要在精确的时间点运行任务,则应将任务归类为精确任务。
大多数任务不需要在精确的时间点运行。通常,任务允许运行的时间点存在细微差异,具体取决于网络可用性和剩余电量等条件。无需在精确时间点运行的任务应归类为延期任务。
1.3.2、推荐的解决方案
下面几部分将介绍针对各个后台任务类型的推荐解决方案。
1.3.2.1、即时任务
对于应在用户离开特定作用域或完成某项互动时结束的任务,我们建议使用Kotlin 协程。许多 Android KTX 库都包含适用于常见应用组件(如 ViewModel
)和常见应用生命周期的现成可用的协程作用域。
如果您是 Java 编程语言用户,请参阅 Android 上的线程处理,了解推荐的选项。
对于应立即执行并需要继续处理的任务,即使用户将应用放在后台运行或重启设备,我们也建议使用WorkManager
并利用其对长时间运行的任务的支持。
在特定情况下(例如使用媒体播放或主动导航功能时),您可能希望直接使用前台服务。
1.3.2.2、延期任务
凡是不直接与用户互动相关切日后随时运行的任务,都可以延期执行。建议为延期任务使用WorkManager
解决方案。
如果您希望某些可延期异步任务即使在退出或设备重启后仍能正常运行,使用WorkManager
可以轻松地调度这些任务。
1.3.2.3、精确任务
需要在精确时间点执行的任务可以使用AlarmManager
。
1.4、构建内容
现在,智能手机的拍照功能基本都很强大。
您将使用Blur-O-Matic,该应用对照片进行模糊处理,并将处理后的照片保存到文件中。
1.5、学习内容
✅ 将WorkManager添加到您的项目中 ✅ 调度简单的任务 ✅ 输入和输出参数 ✅ 链接Work ✅ 唯一Work ✅ 在界面中显示Work状态 ✅ 取消Work ✅ Work约束
二、准备工作
如果愿意,您可以从 GitHub 克隆 WorkManager Codelab:
shell
$ git clone -b start_kotlin https://github.com/googlecodelabs/android-workmanager
运行应用, 您应该会看到下方的屏幕。
屏幕上应该会显示一些单选按钮,您可以通过这些按钮要对图片进行什么程度的模糊处理。按Go按钮即可对图片进行模糊处理并保存。
截止目前,此应用不会应用任何模糊处理。
起始代码包含以下内容:
WorkerUtils
:这个类包含对图片实际进行模糊处理所需的代码,并包含之后您会用于显示Notifications
、将位图保存到图片以及减慢应用运行速度的一些便捷方法。BlurActivity
:*此activity用于显示图片以及添加用于选择模糊程度的单选按钮。BlurViewModel
:*此视图模型用于存储显示BlurActivity
所需的所有数据,也将是您使用WorkManager启动后台工作的类。Constants
:一个静态类,其中包含一些常量。res/activity_blur.xml
:BlurActivity
的布局文件。
三、将WorkManager添加到您的应用
WorkManager
需要使用一下Gradle依赖项,这些依赖项已包含在build文件中:
app/build.gradle
kotlin
dependencies {
// WorkManager dependency
implementation "androidx.work:work-runtime-ktx:$versions.work"
}
build.gradle
ini
version.work = "2.7.1"
四、创建您的第一条WorkRequest
在此步骤中,您将接受res/drawable
文件夹中一张名为android_cupcake.png
的图片,并在后台对这张图片运行一些函数。这些函数会对图片进行模糊处理,然后将图片保存到临时文件中。
4.1、WorkManager基础知识
您需要了解一下几个WorkManager类:
Worker
:此位置用于放置您希望在后台执行的实际工作的代码。您需要扩展此替换doWork()
方法。WorkQuest
:此类表示请求执行某些Work。您将在创建WorkRequest
的过程中传入Worker
。在创建WorkRequst
时,您还可以指定Constraints
等内容,例如运行Worker
的时间。WorkManager
:这个类实质上可以调度WorkRequest
并使其运行。它以一种在系统资源上分散负载的方式调度WorkRequest
,同时遵循您指定的约束条件。
在这种情况下, 您将定义新的BlurWorker
,其中包含用于对图片记性模糊处理的代码。点击Go 按钮时,系统会创建一个WorkRequest
,然后通过WorkManager
将其加入队列。
4.1.1、第1步 - 创建BlurWorker
在workers
软件包中,新建一个名为BlurWorker
的Kotlin类
4.2.1、第2步 - 添加构造函数
向BlurWorker
类添加的Worker
的依赖项:
arduino
class BlurWorker(ctx: Context, params: WorkerParameters): Worker(ctx, params) {
}
4.2.3、第3步 - 替换和实现doWork()
worker
会对所显示的纸杯蛋糕图片进行模糊处理。
为了更好地了解何时执行工作,您将使用WorkerUtil
的makeStatusNotification()
。使用此方法,您可以轻松地在屏幕顶部显示通知横幅。
替换doWork()
方法,然后执行以下操作。您可以参考本部分末尾完成后的代码:
- 通过调用
applicationContext
属性获取Context
。将其分配给名为appContext
的新val
。接下来要执行的各种位图处理需要用到的此参数。 - 使用函数
makeStatusNotification
显示状态通知,以像用户发送有关对图片进行模糊处理的通知。 - 利用纸杯蛋糕图片创建一个
Bitmap
:
ini
val picture = BitmapFactory.decodeResource(appContext.resource, R.drawable.android_cupcake)
- 通过从
WorkerUtils
调用blurBitmap
方法,获取此位图模糊处理后的版本。 - 从
WorkerUtils
调用writeBitmapToFile
方法,将该位图写入临时文件。请务必将返回的URI保存到局部变量。 - 从
WorkerUtils
调用makeStatusNotification
方法,以创建显示URI的通知。 - 返回
Result.success()
。 - 在
try/catch
语句中封装第3-6步的代码。捕获通用的Throwable
。 - 在catch语句中,使用日志语句
Log.e(TAG, "Error applying blur")
输出错误消息。 - 然后再catch语句中返回
Result.failure
此步骤的完整代码如下所示:
BlurWorker.kt
kotlin
package com.example.background.workers
import android.content.Context
import android.graphics.BitmapFactory
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.example.background.R
private const val TAG = "BlurWorker"
class BlurWorker(ctx: Context, params: WorkerParameters): Worker(ctx, params) {
override fun doWork(): Result {
val appContext = applicationContext
makeStatusNotification("Blurring image", appContext)
return try {
val picture = BitmapFactory.decodeResource(
appContext.resource,
R.drawable.android_cupcake
)
val output = blurBitmap(picture, appContext)
// Write bitmap to a temp file
val outputUri = writeBitmapToFile(appContext, output)
makeStatusNotification("Output is @outputUri", appContext)
Result.success()
} catch (throwable: Throwable) {
Log.e(TAG, "Error applying blur")
Result.failure()
}
}
}
4.2.4、第4步 - 在ViewModel中获取WorkManager
在ViewModel
中为WorkManager
实例创建类变量:
BlurViewModel.kt
ini
privagte val workManager = WorkManager.getInstance(application)
4.2.5、第5步 - 在WorkManager中将WorkRequest加入队列
现在是时候设置WorkRequest
并指示WorkManager运行它了。WorkRequest
有两种类型:
OneTimeWorkRequest
: 仅执行一次的WorkRequest
。PeriodicWorkRequest
:按周期重复执行的WorkRequest
。
我们只希望在点击Go 按钮后对图片进行模糊处理。当用户点击Go 按钮时,系统会调用applyBlur
方法,因此请通过BlurWorker
创建OneTimeWorkRequest
。然后,使用WorkManager
实例将您的WorkRequest
加入队列。
将以下代码行添加到BlurViewModel``applyBlur()
方法中:
BlurViewModel.kt
kotlin
internal fun applyBlur(blurLevel: Int) {
workManager.enqueue(OneTimeWorkRequest.from(BlurWorker::class.jaga))
}
4.2.6、第6步 - 运行您的代码!
运行您的代码。此代码应进行编辑,并且在按下Go按钮时,您应该会看到通知。请注意,如需查看更模糊的照片,您应该选择"More blurred"或"the most blurred"选项。
如需确认图片是否已成功模糊,您可以在Android Studio中打开**设备文件浏览器**:
然后依次转到data > data > com.example.background > file > Blathfilterfilter_outputs >URI ,并确认纸杯蛋糕事实上已经模糊:
五、添加输入和输出 对资源目录中的图片资源进行模糊处理固然不错,但如果想让O-M-Matic真正成为一款革命性的图片编辑应用,您应该让用户模糊处理他们在屏幕上看到的图片,然后向他们展示经过模糊处理的图片。
为实现此目标,我们将提供作为输入 显示在WorkRequest
中的纸杯蛋糕图片的URI,然后使用WorkRequest的输入显示 最终的经过模糊处理的图片。
5.1、第1步 - 创建数据输入对象
输入和输出通过Data
对象传入和传出。Data
对象是轻量化的键值对容器。它们用于存储少量可从WorkRequest
传入和传出的数据。
您需要将用户图片的URI传入捆绑包中。该URI存储在名为imageUri
的变量中。
在BlurViewModel
中,创建一个名为createInputDataForUri
的私有方法。该方法应执行以下操作:
- 创建一个
Data.Builder
对象。在收到请求时,导入androidx.work.Data
。 - 如果
imageUri
是非nullURI
,则使用putString
方法将其添加到Data
对象。该方法可获取一个键和一个值。您可以使用Constants
类中的字符串常量KEY_IMAGE_URI
。 - 对
Data.Builder
对象调用build()
以创建Data
对象并返回。
下面是完整的createInputDataForUri
方法:
BlurViewModel.kt
kotlin
/**
* Creates the input data bundle which includes the Uri to operate on
* @return Data which contains the Image Uri as a String
*/
private fun createInputDataForUri(): Data {
val builder = Data.Builder()
imageUri?.let {
builder.putString(KEY_IMAGE_URI, imageUri.toString())
}
return builder.build()
}
5.2、第2步 - 将数据对象传递到WorkRequest
您将更改BlurViewModel
中的appluBlur
方法,以便:
- 创建新的
OneTimeWorkRequestBuilder
。 - 调用
setInputData
,传入createInputDataForUri
的结果 - 构建
OneTimeWorkRequest
. - 使用
WorkManager
将工作请求加入队列,以便系统将可以按照预期运行工作。
下面是完整的applyBlur
方法:
BlurViewModel.kt
kotlin
internal fun applyBlue(blurLevel: Int) {
val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(createInputDataForUri())
.build()
workManager.enqueue(blurRequest)
}
5.3、第3步 - 更新BlurWorker的doWork()以获取输入
现在,请更新BlurWorker
的doWork()
方法,以获取从Data
对象传入的URI:
BlurWorker.kt
kotlin
override fun doWork(): Result {
val appContext = applicationContext
// ADD THIS LINE
val resourceUri = inputData.getString(KEY_IMAGE_URI)
// ... rest of doWork()
}
5.4、第4步 - 对指定URI进行模糊处理
有了此URI,我们现在对屏幕上的纸杯蛋糕图片进行模糊处理。
- 移出之前用于获取图片资源的代码。
val picture = BitmapFactory.decodeResource(appContext.resources, R.drawable.android_cupcake)
- 检查以确认从传入的
Data
获取的resourceUri
不为空。 - 将
picture
变量分配给传入的图片,如下所示:val picture = BitmapFactory.decodeStream(appContext.contentResolver.openInputStream(Uri.parse(resourceUri)))
BlurWork.kt
kotlin
override fun doWork(): Result {
val appContext = applicationContext
val resourceUri = inputData.getString(KEY_IMAGE_URI)
makeStatusNotification("Blurring image", appContext)
return try {
// REMOVE THIS
// val picture = BitmapFactory.decodeResource(appContext.resources, R.drawable.android_cupcake)
if (TextUtils.isEmpty(resourceUri)) {
Log.e(TAG, "Invalid input uri")
throw IllegalArgumentException("Invalid input uri")
}
val resolver = appContext.contentResolver
val picture = BitmapFactory.decodeStream(resolver.openInputStream(Uri.parse(resourceUri)))
val output = blurBitmap(picture, appContext)
//Write bitmap to a temp file
val outputUri = writeBitmapToFile(appContext, output)
Result.success()
} catch (throwable: Throwable) {
Log.e(TAG, "Error appluing blur")
throwable.printStackTrace()
Result.failure()
}
}
5.5、第5步 - 输出临时URI
此工作器的工作已完成,您可以在Result.success()
中返回输出URI。提供作为输出数据的输出URI,以便其他工作器能够轻松访问这张临时图片,执行进一步操作。
- 像对输入进行的操作一样,创建新的
Data
,并将outputUri
存储为String
。使用相同的键,即KEY_IMAGE_URI
。 - 使用
Result.success(Data outputData)
方法将它返回给WorkManager。
BlurWorker.kt 将doWork()
中的Result.success()
行修改为:
scss
val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())
Result.success(outputData)
5.6、第6步 - 运行您的应用
此时,您应该运行应用。它应该进行编译并且行为与您通过设备文件浏览器查看经过模糊处理的图片相同,只是图片尚未显示在屏幕上。
如需检查是否存在其他经过模糊处理的图片,您可以在Android Studio中打开设备文件浏览器 ,然后转到data/data/com.example.background/files/blur_filter_outputs/<URI>
,就像上一步的操作一样。
请注意,您可能需要点击Synchronize
才能查看到图片: