Android WorkManager 完整实战教程(含完整文件)

我将创建一个完整的 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
  1. 依赖配置

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'
}
  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>
  1. 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)
    }
}
  1. 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
        )
    }
}
  1. 工具类

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()
    }
}
  1. 主界面

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 -> {}
        }
    }
}
  1. 布局文件

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>
  1. 使用说明

运行步骤:

  1. 创建项目并复制所有文件
  2. 修改 MyApplication.kt 中的包名
  3. 运行应用,点击各个按钮测试不同的 WorkManager 功能

功能说明:

· 一次性任务:延迟5秒后发送通知

· 周期性任务:每15分钟清理一次缓存

· 带约束的任务:等待设备充电且有网络时执行

· 数据同步:模拟全量/增量数据同步

· 图片上传:模拟图片上传并显示进度

· 链式任务:先同步数据,完成后发送通知

· 清理缓存:一次性清理应用缓存

注意事项:

  1. 周期性任务最小间隔为15分钟(系统限制)
  2. 任务执行时间不要超过10分钟
  3. 输入/输出数据大小限制在10KB以内
  4. 使用 CoroutineWorker 比普通 Worker 更适合执行异步操作

这个完整的示例涵盖了 WorkManager 的大部分常用功能

相关推荐
loitawu2 小时前
Rockchip Android16 系统裁剪指南
android·android16·android裁剪·系统裁剪·rockchip app
小羊子说2 小时前
关于车机中的升级流程小结(SOC、MCU、4G升级流程)
android·adb·性能优化·车载系统
肖。35487870943 小时前
[技巧-11]AndroidManifest.xml完善小技巧。
android
小羊子说3 小时前
Android 车机开发中常用的adb 脚本(更新中)
android·linux·adb·性能优化·车载系统
用户7607495397833 小时前
Android页面四大布局运行结果
android
风往哪边走4 小时前
搜索框自定义
android
用户8249281925364 小时前
把android资源类型详解
android
IT观测4 小时前
深度分析俩款主流移动统计工具Appvue和openinstall
android·java·数据库
用户338675581955 小时前
Android 四种常用布局完全解析(附实战项目截图)
android