导入
def work_version = "2.6.0"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
使用
class TestWorker(appContext: Context, workerParams: WorkerParameters): Worker(appContext,workerParams) {
override fun doWork(): Result {
Log.d("haha",Thread.currentThread().name)
return Result.success()
}
}
执行一次的任务
val testWorkerRequest1 = OneTimeWorkRequestBuilder()
// Additional configuration
.build()
val testWorkerRequest2 = OneTimeWorkRequest.from(TestWorker::class.java)
WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest1)
}
执行定期的任务
定期任务最低定义间隔为15分钟
val workRequest =
PeriodicWorkRequestBuilder(1, TimeUnit.HOURS)
// Additional configuration
build()
WorkManager.getInstance(requireContext()).enqueue(workRequest)
工作约束
约束将工作延迟到满足最佳条件时运行。
NetworkType
约束运行工作所需的[网络类型]。例如 Wi-Fi UNMETERED
BatteryNotLow
如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
RequiresCharging
如果设置为 true,那么工作只能在设备充电时运行。
DeviceIdle
如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议使用此约束。
StorageNotLow
如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val workerRequest: WorkRequest =
OneTimeWorkRequestBuilder()
.setConstraints(constraints)
.build()
WorkManager.getInstance(requireContext()).enqueue(workerRequest)
重试和退避政策
class TestWorkerRetry (appContext: Context, workerParams: WorkerParameters): Worker(appContext,workerParams) {
override fun doWork(): Result {
Log.d("haha",Thread.currentThread().name)
return Result.retry()
}
}
val myWorkRequest = OneTimeWorkRequestBuilder()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
如果doWork返回retry(),BackoffPolicy.LINEAR会在设置的延时时间后重试,每次增加延时时间,比如设置的延时时间为10秒,如果一直返回retry(),就会在20秒、30秒、40秒依次增加设置的延时时间重试。BackoffPolicy.EXPONENTIAL,每次会以指数级增加下一次重试的时间,比如设置的延时时间为10秒,那么重试时长序列将接近 20、40、80 秒,以此类推。
标记工作
每个工作请求都有一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度。
如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。
例如,WorkManager.cancelAllWorkByTag(String)
会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String)
会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。
val workerRequest: WorkRequest =
OneTimeWorkRequestBuilder()
.addTag("test")
.build()
传入数据
val workerRequest: WorkRequest =
OneTimeWorkRequestBuilder()
.setInputData(workDataOf("key" to "I'm value"))
.build()
class TestWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
val value = inputData.getString("key")
Log.d("haha", value!!)
return Result.success()
}
}
工作状态
一次性的工作状态
对于 one-time
工作请求,工作的初始状态为 ENQUEUED
在 ENQUEUED
状态下,您的工作会在满足其 Constraints
和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING
状态,然后可能会根据工作的结果转为 SUCCEEDED
、FAILED
状态;或者,如果结果是 retry
,它可能会回到 ENQUEUED
状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED
状态。
SUCCEEDED
、FAILED
和 CANCELLED
均表示此工作的终止状态。如果您的工作处于上述任何状态,WorkInfo.State.isFinished()
都将返回 true。
定期工作状态
成功和失败状态仅适用于一次性工作和链式工作。定期工作只有一个终止状态 CANCELLED
。这是因为定期工作永远不会结束。每次运行后,无论结果如何,系统都会重新对其进行调度。
管理工作
唯一工作
有的工作只需要开启一次,需要避免重复启动,可以使用唯一工作。
唯一工作既可用于一次性工作,也可用于定期工作。可以通过调用以下方法之一创建唯一工作序列,具体取决于是调度重复工作还是一次性工作。
WorkManager.enqueueUniqueWork()
WorkManager.enqueueUniquePeriodicWork()`
val testWorkerRequest =
PeriodicWorkRequestBuilder(900000, TimeUnit.SECONDS).build()
WorkManager.getInstance(requireContext()).enqueueUniquePeriodicWork(
"testwork",
ExistingPeriodicWorkPolicy.KEEP,
testWorkerRequest
解决冲突政策
ExistingWorkPolicy
它支持用于处理冲突的 4 个选项。
REPLACE
:用新工作替换现有工作。此选项将取消现有工作。
KEEP
:保留现有工作,并忽略新工作。
APPEND
:将新工作附加到现有工作的末尾。此政策将导致您的新工作链接到现有工作,在现有工作完成后运行。
现有工作将成为新工作的先决条件。如果现有工作变为 CANCELLED
或 FAILED
状态,新工作也会变为 CANCELLED
或 FAILED
。如果您希望无论现有工作的状态如何都运行新工作,请改用 APPEND_OR_REPLACE
。
APPEND_OR_REPLACE
函数类似于 APPEND
,不过它并不依赖于先决条件工作状态。即使现有工作变为 CANCELLED
或 FAILED
状态,新工作仍会运行。
对于定期工作,需要提供一个 ExistingPeriodicWorkPolicy
,它支持 REPLACE
和 KEEP
这两个选项。这些选项的功能与其对应的 ExistingWorkPolicy 功能相同。
观察工作
// by id
workManager.getWorkInfoByIdLiveData(syncWorker.id) // LiveData
// by name
workManager.getWorkInfosForUniqueWork("sync") // LiveData>
// by tag
workManager.getWorkInfosByTag("syncTag") // LiveData>
val testWorkerRequest = OneTimeWorkRequestBuilder().build()
WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)
val listener = WorkManager.getInstance(requireContext())
.getWorkInfoByIdLiveData(testWorkerRequest.id)
listener.observe(viewLifecycleOwner, object : Observer {
override fun onChanged(t: WorkInfo?) {
Log.d("haha", t!!.state.name)
}
})
高级用法
自定义初始化
在manifest里面移除默认的WorkManagerInitializer,在Application中实现Configuration.Provider,可以对WorkManager进行配置,比如设置线程池
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder().setExecutor(Executors.newSingleThreadExecutor()).build()
}
}
在Worker处理线程
WorkManager 会自动在后台线程中调用 Worker.doWork()
,可以初始化的时候给WorkManager配置线程池或者不配置,系统会默认给一个线程池。Worker.doWork()
是同步调用,内部调用方法需要调用同步方法。如果需要在内部调用异步回调操作的方法,需要使用ListenableWorker
使用CoroutineWorker
class TestWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d("haha", "start" + Thread.currentThread().name)
delay(5000)
Log.d("haha", "worker" + Thread.currentThread().name)
return Result.success()
}
}
在ListenableWorker处理
需要处理基于回调的异步操作。在这种情况下,不能只依靠 Worker
来完成操作,因为它无法以阻塞方式完成这项工作。WorkManager 通过 ListenableWorker
支持该用
使用councurrent-futures
包含到 gradle 文件中并使用 CallbackToFutureAdapter
。
implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0"
import android.content.Context
import android.util.Log
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters
import com.google.common.util.concurrent.ListenableFuture
import okhttp3.*
import java.io.IOException
class CallbackWorker(
context: Context,
params: WorkerParameters
) : ListenableWorker(context, params) {
override fun startWork(): ListenableFuture {
return CallbackToFutureAdapter.getFuture { completer ->
val callback = object : Callback {
var successes = 0
override fun onFailure(call: Call, e: IOException) {
Log.d("onFailure", e.message + "")
e.printStackTrace()
completer.setException(e)
}
override fun onResponse(call: Call, response: Response) {
successes++
Log.d("onResponse", successes.toString() + " " + response.body?.string())
if (successes == 100) {
completer.set(Result.success())
}
}
}
repeat(100) {
val client = OkHttpClient();
val request: Request = Request.Builder()
.url("https://www.baidu.com")
.get()
.build()
val call = client.newCall(request)
call.enqueue(callback)
}
callback
}
}
}
val testWorkerRequest =
OneTimeWorkRequestBuilder().build()
WorkManager.getInstance(requireContext()).enqueue(testWorkerRequest)
WorkManager.getInstance(requireContext()).getWorkInfoByIdLiveData(testWorkerRequest.id)
.observe(viewLifecycleOwner, object : Observer {
override fun onChanged(t: WorkInfo?) {
Log.d("haha", t!!.state.name)
}
})
支持长时间运行的工作器
WorkManager 可以向操作系统提供一个信号,指示在此项工作执行期间应尽可能让进程保持活跃状态。这些工作器可以运行超过 10 分钟。这一新功能的示例用例包括批量上传或下载(不可分块)、在本地进行的机器学习模型处理,或者对应用的用户很重要的任务。
ListenableWorker
现在支持 setForegroundAsync()
API,而 CoroutineWorker
则支持挂起 setForeground()
API。这些 API 允许开发者指定此 WorkRequest
是“重要的”(从用户的角度来看)或“长时间运行的”任务。
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.work.*
import kotlinx.coroutines.delay
import com.example.myapplication.R
class TestWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
repeat(100) {
delay(1000)
Log.d(
"haha", "测试长时间任务" + it
)
setForeground(createForegroundInfo(it.toString()))
}
return Result.success()
}
private fun createForegroundInfo(progress: String): ForegroundInfo {
val id = "123"
val title = "测试长时间任务"
val cancel = "取消长时间任务"
// This PendingIntent can be used to cancel the worker
val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())
// Create a Notification channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(id, title, NotificationManager.IMPORTANCE_HIGH)
}
val notification = NotificationCompat.Builder(applicationContext, id)
.setContentTitle(title)
.setTicker(title)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText(progress)
.setOngoing(true)
// Add the cancel action to the notification which can
// be used to cancel the worker
.addAction(android.R.drawable.ic_delete, cancel, intent)
.build()
return ForegroundInfo(123, notification)
}
@TargetApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String, importance: Int) {
val channel = NotificationChannel(channelId, channelName, importance)
val manager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
如果应用以 Android 10(API 级别 29)或更高版本为目标平台,且包含需要位置信息访问权限的长时间运行的工作器,指明该工作器使用 location
的前台服务类型。此外,如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,且包含需要访问相机或麦克风的长时间运行的工作器,请分别声明 camera
或 microphone
前台服务类型。有2种方式申明
在应用的清单中声明工作器的前台服务类型。
在运行时指定前台服务类型
return ForegroundInfo(
123,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
)