在 Android 应用开发中,经常需要处理一些后台任务,比如数据同步、日志上传、图片压缩等。这些任务可能需要在特定条件下执行,也可能需要定期运行。WorkManager 是 Google 推荐的后台任务解决方案,它能确保任务即使在应用退出或设备重启后依然可靠执行。
在 WorkManager 出现之前,Android 开发者需要根据不同场景选择不同方案:
这些 API 的使用场景和兼容性各不相同,WorkManager 统一了这些需求,提供了一套简洁且向后兼容的 API(最低支持到 API 14)。
dependencies{
defwork_version="2.9.0"
implementation"androidx.work:work-runtime-ktx:$work_version"
}classDataSyncWorker(
context:Context,
params:WorkerParameters
):CoroutineWorker(context,params){
overridesuspendfundoWork():Result{
returntry{
// 从 InputData 获取参数
valuserId=inputData.getString("user_id")?:returnResult.failure()
// 执行同步逻辑
valsyncResult=syncUserData(userId)
if(syncResult.success){
// 返回成功,可携带输出数据
valoutputData=workDataOf("sync_time"toSystem.currentTimeMillis())
Result.success(outputData)
}else{
// 返回失败
Result.failure()
}
}catch(e:Exception){
// 返回重试(WorkManager 会自动重试)
Result.retry()
}
}
privatesuspendfunsyncUserData(userId:String):SyncResult{
// 模拟网络请求
delay(2000)
returnSyncResult(success=true)
}
}
dataclassSyncResult(valsuccess:Boolean)// 构建输入数据
valinputData=workDataOf("user_id"to"12345")
// 创建一次性任务请求
valsyncRequest=OneTimeWorkRequestBuilder<DataSyncWorker>()
.setInputData(inputData)
.build()
// 提交任务
WorkManager.getInstance(context).enqueue(syncRequest)WorkManager.getInstance(context)
.getWorkInfoByIdLiveData(syncRequest.id)
.observe(lifecycleOwner){workInfo->
when(workInfo?.state){
WorkInfo.State.ENQUEUED->Log.d("Work","任务已入队")
WorkInfo.State.RUNNING->Log.d("Work","任务执行中")
WorkInfo.State.SUCCEEDED->{
valsyncTime=workInfo.outputData.getLong("sync_time",0)
Log.d("Work","任务成功,同步时间: $syncTime")
}
WorkInfo.State.FAILED->Log.d("Work","任务失败")
WorkInfo.State.CANCELLED->Log.d("Work","任务取消")
else->{}
}
}实际场景中,我们希望任务在特定条件下执行,比如只在 WiFi 网络下上传日志:
valconstraints=Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)// WiFi 或无流量消耗网络
.setRequiresBatteryNotLow(true)// 电量不低
.setRequiresStorageNotLow(true)// 存储空间充足
.setRequiresCharging(false)// 不要求充电
.build()
valuploadRequest=OneTimeWorkRequestBuilder<LogUploadWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(uploadRequest)常见约束类型:
NOT_REQUIRED、CONNECTED、UNMETERED、NOT_ROAMING、METEREDsetRequiresBatteryNotLow(true)setRequiresCharging(true)setRequiresStorageNotLow(true)setRequiresDeviceIdle(true)周期任务的最短间隔是 15 分钟,这是系统为了省电做的限制:
classDataCleanupWorker(
context:Context,
params:WorkerParameters
):CoroutineWorker(context,params){
overridesuspendfundoWork():Result{
returntry{
cleanupOldData()
Result.success()
}catch(e:Exception){
Result.retry()
}
}
privatesuspendfuncleanupOldData(){
valdatabase=AppDatabase.getInstance(applicationContext)
valcutoffTime=System.currentTimeMillis()-7*24*60*60*1000L// 7天前
database.logDao().deleteOlderThan(cutoffTime)
}
}创建周期任务:
valcleanupRequest=PeriodicWorkRequestBuilder<DataCleanupWorker>(
repeatInterval=1,// 间隔
repeatIntervalTimeUnit=TimeUnit.DAYS// 单位
).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"data_cleanup",// 唯一名称
ExistingPeriodicWorkPolicy.KEEP,// 如果已存在则保持
cleanupRequest
)ExistingPeriodicWorkPolicy 选项:
假设需要:下载图片 → 压缩 → 添加水印 → 上传
// 1. 下载图片
valdownloadRequest=OneTimeWorkRequestBuilder<DownloadImageWorker>()
.setInputData(workDataOf("image_url"to"https://example.com/photo.jpg"))
.build()
// 2. 压缩图片
valcompressRequest=OneTimeWorkRequestBuilder<CompressImageWorker>().build()
// 3. 添加水印
valwatermarkRequest=OneTimeWorkRequestBuilder<WatermarkImageWorker>().build()
// 4. 上传图片
valuploadRequest=OneTimeWorkRequestBuilder<UploadImageWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
// 构建链式任务
WorkManager.getInstance(context)
.beginWith(downloadRequest)// 第一步
.then(compressRequest)// 第二步
.then(watermarkRequest)// 第三步
.then(uploadRequest)// 第四步
.enqueue()如果有多个独立任务可以并行执行:
valtaskA=OneTimeWorkRequestBuilder<WorkerA>().build()
valtaskB=OneTimeWorkRequestBuilder<WorkerB>().build()
valtaskC=OneTimeWorkRequestBuilder<WorkerC>().build()
valfinalTask=OneTimeWorkRequestBuilder<FinalWorker>().build()
// taskA 和 taskB 并行执行,都完成后再执行 finalTask
WorkManager.getInstance(context)
.beginWith(listOf(taskA,taskB))
.then(finalTask)
.enqueue()当任务返回 Result.retry() 时,WorkManager 会自动重试,默认使用指数退避策略:
valretryRequest=OneTimeWorkRequestBuilder<RetryableWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,// 或 LINEAR
30,// 初始延迟
TimeUnit.SECONDS
)
.build()使用 enqueueUniqueWork 可以确保同一名称的任务不会重复执行:
valsyncRequest=OneTimeWorkRequestBuilder<DataSyncWorker>().build()
WorkManager.getInstance(context).enqueueUniqueWork(
"user_data_sync",// 唯一名称
ExistingWorkPolicy.KEEP,// 策略
syncRequest
)ExistingWorkPolicy 选项:
// 根据 ID 取消
WorkManager.getInstance(context).cancelWorkById(workRequest.id)
// 根据唯一名称取消
WorkManager.getInstance(context).cancelUniqueWork("user_data_sync")
// 根据 Tag 取消
WorkManager.getInstance(context).cancelAllWorkByTag("sync")
// 取消所有任务
WorkManager.getInstance(context).cancelAllWork()场景:App 需要在网络可用时上报用户行为日志,要求:
classLogUploadWorker(
context:Context,
params:WorkerParameters
):CoroutineWorker(context,params){
overridesuspendfundoWork():Result{
valdatabase=AppDatabase.getInstance(applicationContext)
valpendingLogs=database.logDao().getPendingLogs()
if(pendingLogs.isEmpty()){
returnResult.success()
}
returntry{
valapi=RetrofitClient.create<LogApiService>()
valresponse=api.uploadLogs(pendingLogs)
if(response.isSuccessful){
database.logDao().markAsUploaded(pendingLogs.map{it.id})
Result.success()
}else{
Result.retry()
}
}catch(e:Exception){
Result.retry()
}
}
}
// 提交周期任务
funscheduleLogUpload(context:Context){
valconstraints=Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.build()
valuploadRequest=PeriodicWorkRequestBuilder<LogUploadWorker>(
1,TimeUnit.HOURS
)
.setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
15,TimeUnit.MINUTES
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"log_upload",
ExistingPeriodicWorkPolicy.KEEP,
uploadRequest
)
}WorkManager.getInstance(context)
.getWorkInfosByTagLiveData("sync")
.observe(lifecycleOwner){workInfoList->
workInfoList.forEach{workInfo->
Log.d("Work","ID: ${workInfo.id}, State: ${workInfo.state}")
}
}在 Application 类中配置:
classMyApp:Application(),Configuration.Provider{
overridefungetWorkManagerConfiguration()=
Configuration.Builder()
.setMinimumLoggingLevel(Log.DEBUG)
.build()
}记得在 AndroidManifest.xml 中禁用默认初始化:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove"/>WorkManager 不是实时任务调度器,系统会根据约束条件和设备状态决定执行时机。如果需要立即执行任务,使用 setExpedited() 标记为加急任务(API 23+)。
系统为了省电会批量处理任务,实际执行时间可能晚于预期。如果需要精确定时,考虑使用 AlarmManager + BroadcastReceiver。
通过依赖注入(Hilt)或者自定义 WorkerFactory:
classDaggerWorkerFactory@Injectconstructor(
privatevalrepository:UserRepository
):WorkerFactory(){
overridefuncreateWorker(
appContext:Context,
workerClassName:String,
workerParameters:WorkerParameters
):ListenableWorker? {
returnwhen(workerClassName){
DataSyncWorker::class.java.name->
DataSyncWorker(appContext,workerParameters,repository)
else->null
}
}
}WorkManager 是 Android 后台任务的推荐方案,适用于需要保证执行的场景。关键点:
OneTimeWorkRequest,周期任务用 PeriodicWorkRequestConstraints 设置执行条件对于需要立即执行、精确定时或需要前台服务的场景,WorkManager 可能不是最佳选择,需要结合 Coroutines、AlarmManager 或 Foreground Service 等方案。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。