我将创建一个完整的 Android 应用示例,展示 WorkManager 的各种用法。
项目结构
app/
├── src/main/
│ ├── java/com/example/workmanagerdemo/
│ │ ├── MainActivity.kt
│ │ ├── workers/
│ │ │ ├── DataSyncWorker.kt
│ │ │ ├── ImageUploadWorker.kt
│ │ │ ├── CacheCleanupWorker.kt
│ │ │ └── NotificationWorker.kt
│ │ ├── repository/
│ │ │ └── DataRepository.kt
│ │ ├── utils/
│ │ │ └── WorkManagerUtils.kt
│ │ └── MyApplication.kt
│ └── res/
│ ├── layout/
│ │ └── activity_main.xml
│ └── values/
│ └── strings.xml
- 依赖配置
app/build.gradle
kotlin
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'kotlin-parcelize'
}
android {
namespace 'com.example.workmanagerdemo'
compileSdk 34
defaultConfig {
applicationId "com.example.workmanagerdemo"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// WorkManager
implementation 'androidx.work:work-runtime-ktx:2.9.0'
// ViewModel and LiveData
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
// Retrofit for network calls
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
// Room Database
implementation 'androidx.room:room-runtime:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
kapt 'androidx.room:room-compiler:2.6.1'
// Image loading
implementation 'com.github.bumptech.glide:glide:4.16.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
- Application 类
MyApplication.kt
kotlin
package com.example.workmanagerdemo
import android.app.Application
import android.util.Log
import androidx.work.Configuration
import androidx.work.WorkManager
import java.util.concurrent.Executors
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 自定义 WorkManager 配置
val config = Configuration.Builder()
.setMinimumLoggingLevel(Log.DEBUG) // 设置日志级别
.setExecutor(Executors.newFixedThreadPool(4)) // 自定义线程池
.setJobSchedulerJobIdRange(0, 1000) // JobScheduler ID 范围
.build()
// 初始化 WorkManager(可选,默认会自动初始化)
WorkManager.initialize(this, config)
}
}
AndroidManifest.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 可选:用于精确计时 -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.WorkManagerDemo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- WorkManager 的初始内容提供者(自动配置) -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>
- Worker 实现类
workers/DataSyncWorker.kt
kotlin
package com.example.workmanagerdemo.workers
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.example.workmanagerdemo.repository.DataRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
class DataSyncWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
const val KEY_USER_ID = "user_id"
const val KEY_SYNC_TYPE = "sync_type"
const val KEY_SYNCED_ITEMS = "synced_items"
const val KEY_SYNC_TIME = "sync_time"
const val TAG = "DataSyncWorker"
}
private val repository = DataRepository(context)
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
// 获取输入参数
val userId = inputData.getInt(KEY_USER_ID, 0)
val syncType = inputData.getString(KEY_SYNC_TYPE) ?: "full"
Log.d(TAG, "开始同步数据 - UserId: $userId, Type: $syncType")
// 模拟进度更新
setProgress(workDataOf("progress" to 0))
// 执行不同的同步策略
val syncedItems = when (syncType) {
"full" -> performFullSync(userId)
"incremental" -> performIncrementalSync(userId)
else -> performPartialSync(userId)
}
setProgress(workDataOf("progress" to 100))
// 准备输出数据
val outputData = Data.Builder()
.putInt(KEY_SYNCED_ITEMS, syncedItems)
.putLong(KEY_SYNC_TIME, System.currentTimeMillis())
.build()
Log.d(TAG, "同步完成 - 同步项: $syncedItems")
Result.success(outputData)
} catch (e: Exception) {
Log.e(TAG, "同步失败", e)
// 判断是否需要重试
when (e) {
is java.net.UnknownHostException,
is java.net.SocketTimeoutException -> {
// 网络错误,可以重试
Result.retry()
}
else -> {
// 其他错误,失败
Result.failure()
}
}
}
}
}
private suspend fun performFullSync(userId: Int): Int {
// 模拟全量同步
delay(2000)
return 100 // 返回同步的项目数量
}
private suspend fun performIncrementalSync(userId: Int): Int {
// 模拟增量同步
delay(1000)
return 10
}
private suspend fun performPartialSync(userId: Int): Int {
// 模拟部分同步
delay(1500)
return 25
}
}
workers/ImageUploadWorker.kt
kotlin
package com.example.workmanagerdemo.workers
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import kotlinx.coroutines.delay
import java.io.File
import java.io.FileOutputStream
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class ImageUploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
const val KEY_IMAGE_PATH = "image_path"
const val KEY_IMAGE_URI = "image_uri"
const val KEY_UPLOAD_URL = "upload_url"
const val KEY_UPLOAD_ID = "upload_id"
const val TAG = "ImageUploadWorker"
}
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
// 获取图片路径
val imagePath = inputData.getString(KEY_IMAGE_PATH)
val imageUri = inputData.getString(KEY_IMAGE_URI)
val uploadUrl = inputData.getString(KEY_UPLOAD_URL) ?: "https://api.example.com/upload"
Log.d(TAG, "开始上传图片 - Path: $imagePath, Uri: $imageUri")
// 处理图片(压缩)
val compressedImage = compressImage(imagePath ?: imageUri ?: "")
// 模拟上传进度
for (progress in 0..100 step 20) {
setProgress(workDataOf("upload_progress" to progress))
delay(500)
}
// 模拟上传到服务器
val uploadId = uploadToServer(compressedImage, uploadUrl)
// 准备输出数据
val outputData = Data.Builder()
.putString(KEY_UPLOAD_ID, uploadId)
.build()
Log.d(TAG, "图片上传完成 - UploadId: $uploadId")
Result.success(outputData)
} catch (e: Exception) {
Log.e(TAG, "图片上传失败", e)
Result.failure()
}
}
}
private suspend fun compressImage(imagePath: String): ByteArray {
// 压缩图片
val bitmap = BitmapFactory.decodeFile(imagePath)
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 1080, 1920, true)
val outputStream = FileOutputStream(File(applicationContext.cacheDir, "compressed.jpg"))
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
outputStream.close()
return File(applicationContext.cacheDir, "compressed.jpg").readBytes()
}
private suspend fun uploadToServer(imageData: ByteArray, uploadUrl: String): String {
// 模拟网络上传
delay(3000)
return "upload_${System.currentTimeMillis()}"
}
}
workers/CacheCleanupWorker.kt
kotlin
package com.example.workmanagerdemo.workers
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class CacheCleanupWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
const val TAG = "CacheCleanupWorker"
}
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
Log.d(TAG, "开始清理缓存")
// 清理应用缓存
val cleanedSize = clearCache()
// 清理 Glide 缓存
clearGlideCache()
// 清理临时文件
clearTempFiles()
Log.d(TAG, "缓存清理完成 - 清理了 ${cleanedSize / 1024} KB")
Result.success()
} catch (e: Exception) {
Log.e(TAG, "缓存清理失败", e)
Result.failure()
}
}
}
private suspend fun clearCache(): Long {
val cacheDir = applicationContext.cacheDir
return deleteRecursive(cacheDir)
}
private suspend fun clearGlideCache() {
// 清理 Glide 缓存
try {
val glideCacheDir = File(applicationContext.cacheDir, "glide")
if (glideCacheDir.exists()) {
deleteRecursive(glideCacheDir)
}
} catch (e: Exception) {
Log.e(TAG, "清理 Glide 缓存失败", e)
}
}
private suspend fun clearTempFiles() {
// 清理临时文件
val tempDir = File(applicationContext.filesDir, "temp")
if (tempDir.exists()) {
deleteRecursive(tempDir)
}
}
private fun deleteRecursive(file: File): Long {
var size = 0L
if (file.isDirectory) {
val children = file.listFiles()
children?.forEach {
size += deleteRecursive(it)
}
}
size += file.length()
file.delete()
return size
}
}
workers/NotificationWorker.kt
kotlin
package com.example.workmanagerdemo.workers
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.example.workmanagerdemo.R
import kotlinx.coroutines.delay
class NotificationWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
companion object {
const val CHANNEL_ID = "work_manager_channel"
const val CHANNEL_NAME = "Work Manager Notifications"
const val NOTIFICATION_ID = 1001
}
override suspend fun doWork(): Result {
// 模拟处理数据
delay(2000)
// 发送通知
sendNotification("任务完成", "您的后台任务已成功执行")
return Result.success()
}
private fun sendNotification(title: String, message: String) {
val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
// 构建通知
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setAutoCancel(true)
.build()
// 发送通知
notificationManager.notify(NOTIFICATION_ID, notification)
}
}
- Repository 层
repository/DataRepository.kt
kotlin
package com.example.workmanagerdemo.repository
import android.content.Context
import androidx.work.*
import com.example.workmanagerdemo.workers.DataSyncWorker
import com.example.workmanagerdemo.workers.ImageUploadWorker
import java.util.concurrent.TimeUnit
class DataRepository(private val context: Context) {
private val workManager = WorkManager.getInstance(context)
/**
* 执行数据同步
*/
fun syncData(
userId: Int,
syncType: String = "full",
onResult: (WorkInfo?) -> Unit
) {
// 创建输入数据
val inputData = workDataOf(
DataSyncWorker.KEY_USER_ID to userId,
DataSyncWorker.KEY_SYNC_TYPE to syncType
)
// 设置约束条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
// 创建 WorkRequest
val syncWork = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setConstraints(constraints)
.setInputData(inputData)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
30, TimeUnit.SECONDS
)
.build()
// 执行任务并观察结果
workManager.enqueue(syncWork)
workManager.getWorkInfoByIdLiveData(syncWork.id)
.observeForever { workInfo ->
onResult(workInfo)
}
}
/**
* 执行图片上传
*/
fun uploadImage(
imagePath: String,
uploadUrl: String,
onProgress: (Int) -> Unit,
onComplete: (String?) -> Unit
) {
val inputData = workDataOf(
ImageUploadWorker.KEY_IMAGE_PATH to imagePath,
ImageUploadWorker.KEY_UPLOAD_URL to uploadUrl
)
val uploadWork = OneTimeWorkRequestBuilder<ImageUploadWorker>()
.setInputData(inputData)
.build()
workManager.enqueue(uploadWork)
// 监听进度
workManager.getWorkInfoByIdLiveData(uploadWork.id)
.observeForever { workInfo ->
when (workInfo.state) {
WorkInfo.State.RUNNING -> {
val progress = workInfo.progress.getInt("upload_progress", 0)
onProgress(progress)
}
WorkInfo.State.SUCCEEDED -> {
val uploadId = workInfo.outputData.getString(ImageUploadWorker.KEY_UPLOAD_ID)
onComplete(uploadId)
}
WorkInfo.State.FAILED -> {
onComplete(null)
}
else -> {}
}
}
}
/**
* 启动周期性缓存清理
*/
fun startPeriodicCacheCleanup() {
val constraints = Constraints.Builder()
.setRequiresBatteryNotLow(true)
.setRequiresDeviceIdle(true)
.build()
val cleanupWork = PeriodicWorkRequestBuilder<CacheCleanupWorker>(
24, TimeUnit.HOURS, // 每24小时
4, TimeUnit.HOURS // 灵活期4小时
)
.setConstraints(constraints)
.build()
workManager.enqueueUniquePeriodicWork(
"cache_cleanup",
ExistingPeriodicWorkPolicy.KEEP,
cleanupWork
)
}
}
- 工具类
utils/WorkManagerUtils.kt
kotlin
package com.example.workmanagerdemo.utils
import android.content.Context
import androidx.work.*
import com.example.workmanagerdemo.workers.DataSyncWorker
import com.example.workmanagerdemo.workers.ImageUploadWorker
import com.example.workmanagerdemo.workers.NotificationWorker
import java.util.concurrent.TimeUnit
object WorkManagerUtils {
/**
* 获取 WorkManager 实例
*/
fun getInstance(context: Context): WorkManager {
return WorkManager.getInstance(context)
}
/**
* 检查任务是否正在运行
*/
suspend fun isWorkRunning(context: Context, workName: String): Boolean {
val workManager = getInstance(context)
val workInfos = workManager.getWorkInfosForUniqueWork(workName).get()
return workInfos.any { workInfo ->
workInfo.state == WorkInfo.State.RUNNING ||
workInfo.state == WorkInfo.State.ENQUEUED
}
}
/**
* 取消所有任务
*/
fun cancelAllWork(context: Context) {
getInstance(context).cancelAllWork()
}
/**
* 取消特定任务
*/
fun cancelWork(context: Context, workName: String) {
getInstance(context).cancelUniqueWork(workName)
}
/**
* 获取所有任务状态
*/
fun getAllWorkStatus(context: Context) {
val workManager = getInstance(context)
// 获取所有未完成的任务
val workQuery = WorkQuery.Builder
.fromStates(
WorkInfo.State.ENQUEUED,
WorkInfo.State.RUNNING,
WorkInfo.State.BLOCKED
)
.build()
workManager.getWorkInfos(workQuery)
.get()
.forEach { workInfo ->
println("Work ID: ${workInfo.id}, State: ${workInfo.state}")
}
}
/**
* 延迟执行任务
*/
fun scheduleDelayedWork(
context: Context,
delay: Long,
unit: TimeUnit
) {
val delayedWork = OneTimeWorkRequestBuilder<NotificationWorker>()
.setInitialDelay(delay, unit)
.build()
getInstance(context).enqueue(delayedWork)
}
/**
* 创建链式任务
*/
fun createWorkChain(context: Context) {
val workManager = getInstance(context)
// 创建三个任务
val work1 = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setInputData(workDataOf(DataSyncWorker.KEY_SYNC_TYPE to "full"))
.build()
val work2 = OneTimeWorkRequestBuilder<ImageUploadWorker>()
.build()
val work3 = OneTimeWorkRequestBuilder<NotificationWorker>()
.build()
// 链式执行
workManager
.beginWith(work1)
.then(work2)
.then(work3)
.enqueue()
}
}
- 主界面
MainActivity.kt
kotlin
package com.example.workmanagerdemo
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.work.*
import com.example.workmanagerdemo.databinding.ActivityMainBinding
import com.example.workmanagerdemo.repository.DataRepository
import com.example.workmanagerdemo.utils.WorkManagerUtils
import com.example.workmanagerdemo.workers.*
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var repository: DataRepository
private lateinit var workManager: WorkManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
repository = DataRepository(this)
workManager = WorkManager.getInstance(this)
setupClickListeners()
setupObservers()
}
private fun setupClickListeners() {
// 一次性任务
binding.btnOneTimeWork.setOnClickListener {
executeOneTimeWork()
}
// 周期性任务
binding.btnPeriodicWork.setOnClickListener {
executePeriodicWork()
}
// 带约束的任务
binding.btnConstrainedWork.setOnClickListener {
executeConstrainedWork()
}
// 数据同步
binding.btnDataSync.setOnClickListener {
executeDataSync()
}
// 图片上传
binding.btnImageUpload.setOnClickListener {
executeImageUpload()
}
// 链式任务
binding.btnChainWork.setOnClickListener {
executeChainWork()
}
// 查看任务状态
binding.btnViewStatus.setOnClickListener {
viewWorkStatus()
}
// 取消任务
binding.btnCancelWork.setOnClickListener {
cancelAllWork()
}
// 清理缓存
binding.btnCleanCache.setOnClickListener {
executeCacheCleanup()
}
}
private fun setupObservers() {
// 观察任务状态
workManager.getWorkInfosForUniqueWorkLiveData("data_sync")
.observe(this) { workInfos ->
workInfos.firstOrNull()?.let { workInfo ->
updateUIForWorkState(workInfo.state)
}
}
}
private fun executeOneTimeWork() {
val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>()
.setInitialDelay(5, TimeUnit.SECONDS)
.build()
workManager.enqueue(workRequest)
Toast.makeText(this, "5秒后执行一次性任务", Toast.LENGTH_SHORT).show()
// 观察任务状态
workManager.getWorkInfoByIdLiveData(workRequest.id)
.observe(this) { workInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
Toast.makeText(this, "一次性任务执行成功", Toast.LENGTH_SHORT).show()
}
WorkInfo.State.FAILED -> {
Toast.makeText(this, "一次性任务执行失败", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
}
private fun executePeriodicWork() {
// 注意:最小间隔为15分钟
val periodicWork = PeriodicWorkRequestBuilder<CacheCleanupWorker>(
15, TimeUnit.MINUTES,
5, TimeUnit.MINUTES
).build()
workManager.enqueueUniquePeriodicWork(
"periodic_cleanup",
ExistingPeriodicWorkPolicy.KEEP,
periodicWork
)
Toast.makeText(this, "周期性任务已启动(每15分钟)", Toast.LENGTH_SHORT).show()
}
private fun executeConstrainedWork() {
// 设置约束条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.setRequiresBatteryNotLow(true)
.build()
val workRequest = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setConstraints(constraints)
.setInputData(workDataOf(
DataSyncWorker.KEY_USER_ID to 123,
DataSyncWorker.KEY_SYNC_TYPE to "full"
))
.build()
workManager.enqueue(workRequest)
Toast.makeText(this, "等待满足条件后执行任务(充电+网络+电量充足)", Toast.LENGTH_SHORT).show()
}
private fun executeDataSync() {
repository.syncData(123, "full") { workInfo ->
workInfo?.let {
when (it.state) {
WorkInfo.State.RUNNING -> {
binding.tvStatus.text = "数据同步中..."
}
WorkInfo.State.SUCCEEDED -> {
val syncedItems = it.outputData.getInt(DataSyncWorker.KEY_SYNCED_ITEMS, 0)
binding.tvStatus.text = "同步完成:$syncedItems 项数据"
Toast.makeText(this, "同步完成:$syncedItems 项", Toast.LENGTH_SHORT).show()
}
WorkInfo.State.FAILED -> {
binding.tvStatus.text = "同步失败"
Toast.makeText(this, "同步失败,请重试", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
}
}
private fun executeImageUpload() {
// 模拟图片路径
val imagePath = "/sdcard/Pictures/sample.jpg"
repository.uploadImage(
imagePath = imagePath,
uploadUrl = "https://api.example.com/upload",
onProgress = { progress ->
binding.progressBar.progress = progress
binding.tvProgress.text = "上传进度: $progress%"
},
onComplete = { uploadId ->
if (uploadId != null) {
binding.tvStatus.text = "上传成功: $uploadId"
Toast.makeText(this, "图片上传成功", Toast.LENGTH_SHORT).show()
} else {
binding.tvStatus.text = "上传失败"
Toast.makeText(this, "图片上传失败", Toast.LENGTH_SHORT).show()
}
}
)
Toast.makeText(this, "开始上传图片", Toast.LENGTH_SHORT).show()
}
private fun executeChainWork() {
// 创建链式任务
val work1 = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setInputData(workDataOf(
DataSyncWorker.KEY_SYNC_TYPE to "incremental"
))
.build()
val work2 = OneTimeWorkRequestBuilder<NotificationWorker>()
.build()
workManager
.beginWith(work1)
.then(work2)
.enqueue()
Toast.makeText(this, "链式任务已启动(先同步数据,然后发送通知)", Toast.LENGTH_SHORT).show()
// 观察第一个任务
workManager.getWorkInfoByIdLiveData(work1.id)
.observe(this) { workInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
binding.tvStatus.text = "数据同步完成,准备发送通知"
}
WorkInfo.State.FAILED -> {
binding.tvStatus.text = "数据同步失败,链式任务中断"
}
else -> {}
}
}
}
private fun viewWorkStatus() {
lifecycleScope.launch {
val isRunning = WorkManagerUtils.isWorkRunning(this@MainActivity, "data_sync")
binding.tvStatus.text = if (isRunning) {
"数据同步任务正在运行中"
} else {
"没有正在运行的数据同步任务"
}
}
// 获取所有任务状态
WorkManagerUtils.getAllWorkStatus(this)
}
private fun cancelAllWork() {
WorkManagerUtils.cancelAllWork(this)
binding.tvStatus.text = "所有任务已取消"
Toast.makeText(this, "已取消所有后台任务", Toast.LENGTH_SHORT).show()
}
private fun executeCacheCleanup() {
val cleanupWork = OneTimeWorkRequestBuilder<CacheCleanupWorker>()
.build()
workManager.enqueue(cleanupWork)
Toast.makeText(this, "开始清理缓存", Toast.LENGTH_SHORT).show()
workManager.getWorkInfoByIdLiveData(cleanupWork.id)
.observe(this) { workInfo ->
when (workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
Toast.makeText(this, "缓存清理完成", Toast.LENGTH_SHORT).show()
binding.tvStatus.text = "缓存已清理"
}
WorkInfo.State.FAILED -> {
Toast.makeText(this, "缓存清理失败", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
}
private fun updateUIForWorkState(state: WorkInfo.State) {
when (state) {
WorkInfo.State.ENQUEUED -> {
binding.tvStatus.text = "任务已加入队列"
}
WorkInfo.State.RUNNING -> {
binding.tvStatus.text = "任务执行中..."
}
WorkInfo.State.SUCCEEDED -> {
binding.tvStatus.text = "任务执行成功"
}
WorkInfo.State.FAILED -> {
binding.tvStatus.text = "任务执行失败"
}
WorkInfo.State.CANCELLED -> {
binding.tvStatus.text = "任务已取消"
}
else -> {}
}
}
}
- 布局文件
res/layout/activity_main.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 标题 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="WorkManager 演示"
android:textSize="24sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="16dp" />
<!-- 状态显示 -->
<TextView
android:id="@+id/tvStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="就绪"
android:textSize="14sp"
android:gravity="center"
android:padding="8dp"
android:background="@android:drawable/editbox_background"
android:layout_marginBottom="16dp" />
<!-- 进度条 -->
<TextView
android:id="@+id/tvProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="上传进度: 0%"
android:gravity="center"
android:layout_marginBottom="8dp" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
<!-- 按钮分组 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="基础功能"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnOneTimeWork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="一次性任务"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnPeriodicWork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="周期性任务"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnConstrainedWork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="带约束的任务"
android:layout_marginBottom="8dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="数据操作"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnDataSync"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="数据同步"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnImageUpload"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="图片上传"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnCleanCache"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="清理缓存"
android:layout_marginBottom="8dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="高级功能"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnChainWork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="链式任务"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnViewStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查看任务状态"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnCancelWork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消所有任务"
android:layout_marginBottom="8dp" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
res/values/strings.xml
xml
<resources>
<string name="app_name">WorkManager Demo</string>
</resources>
res/values/themes.xml
xml
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.WorkManagerDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
</style>
</resources>
- 使用说明
运行步骤:
- 创建项目并复制所有文件
- 修改 MyApplication.kt 中的包名
- 运行应用,点击各个按钮测试不同的 WorkManager 功能
功能说明:
· 一次性任务:延迟5秒后发送通知
· 周期性任务:每15分钟清理一次缓存
· 带约束的任务:等待设备充电且有网络时执行
· 数据同步:模拟全量/增量数据同步
· 图片上传:模拟图片上传并显示进度
· 链式任务:先同步数据,完成后发送通知
· 清理缓存:一次性清理应用缓存
注意事项:
- 周期性任务最小间隔为15分钟(系统限制)
- 任务执行时间不要超过10分钟
- 输入/输出数据大小限制在10KB以内
- 使用 CoroutineWorker 比普通 Worker 更适合执行异步操作
这个完整的示例涵盖了 WorkManager 的大部分常用功能