在这里插入图片描述
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

项目概述

后台任务与定时任务是现代应用实现长期运行操作的重要方式。在 KMP 框架下,我们需要实现跨平台的后台任务调度、定时任务执行和唤醒锁管理。本文详细介绍了如何在 Kotlin Multiplatform 中实现高效的后台任务系统,包括 WorkManager、定时器和鸿蒙系统特定的后台任务处理方式。

典型场景包括:

  • 数据同步与日志上报:在后台定期同步本地数据、上报埋点和崩溃信息;
  • 定时提醒与推送预处理:在特定时间点触发任务,如日程提醒、订阅更新;
  • 长时间运行任务:如音视频转码、大文件上传/下载、缓存预热等。

本篇的目标是:

  • commonMain 中抽象统一的 BackgroundTaskManagerScheduledTaskManager 接口;
  • 封装任务的类型、约束、重试策略和状态回调;
  • 在 Android、iOS、鸿蒙和 Web 中利用各自平台的任务调度机制实现同一套语义。

第一部分:后台任务核心概念

后台任务架构

// commonMain/kotlin/com/example/task/BackgroundTaskManager.kt
expect class BackgroundTaskManager {
    suspend fun scheduleTask(task: BackgroundTask): String
    suspend fun cancelTask(taskId: String): Boolean
    suspend fun getTaskStatus(taskId: String): TaskStatus?
    suspend fun getAllTasks(): List<BackgroundTask>
    fun addListener(listener: TaskListener)
}

data class BackgroundTask(
    val id: String,
    val name: String,
    val description: String,
    val type: TaskType,
    val priority: TaskPriority,
    val constraints: TaskConstraints,
    val retryPolicy: RetryPolicy,
    val timeout: Long = 300000 // 5 minutes
)

enum class TaskType {
    ONE_TIME, PERIODIC, DELAYED
}

enum class TaskPriority {
    LOW, NORMAL, HIGH, CRITICAL
}

data class TaskConstraints(
    val requiresNetwork: Boolean = false,
    val requiresCharging: Boolean = false,
    val requiresBatteryNotLow: Boolean = false,
    val requiresDeviceIdle: Boolean = false,
    val requiresStorageNotLow: Boolean = false
)

data class RetryPolicy(
    val maxRetries: Int = 3,
    val backoffPolicy: BackoffPolicy = BackoffPolicy.EXPONENTIAL,
    val initialDelay: Long = 1000
)

enum class BackoffPolicy {
    LINEAR, EXPONENTIAL
}

enum class TaskStatus {
    PENDING, RUNNING, COMPLETED, FAILED, CANCELLED
}

interface TaskListener {
    fun onTaskStarted(taskId: String)
    fun onTaskCompleted(taskId: String, result: Any?)
    fun onTaskFailed(taskId: String, error: String)
    fun onTaskCancelled(taskId: String)
}

这一部分定义了跨平台的后台任务抽象

  • BackgroundTaskManager 负责任务的安排、取消、查询状态等;
  • BackgroundTask 描述了一项任务的元信息,包括类型(一次性/周期/延迟)、优先级、约束条件和重试策略;
  • TaskConstraints / RetryPolicy 等结构把“在什么条件下执行任务”“失败如何退避重试”统一建模;
  • TaskListener 将任务的生命周期事件(开始/完成/失败/取消)暴露给业务,用于更新 UI 或记录日志。

通过这套抽象,可以在 common 层设计统一的“后台任务清单”和“任务调度策略”,而具体的实现则由各平台的 actual 类完成。

定时任务架构

// commonMain/kotlin/com/example/task/ScheduledTaskManager.kt
expect class ScheduledTaskManager {
    suspend fun scheduleRepeating(task: ScheduledTask): String
    suspend fun cancelSchedule(taskId: String): Boolean
    suspend fun updateSchedule(taskId: String, schedule: Schedule): Boolean
    fun addListener(listener: ScheduleListener)
}

data class ScheduledTask(
    val id: String,
    val name: String,
    val schedule: Schedule,
    val action: suspend () -> Unit
)

sealed class Schedule {
    data class Interval(val intervalMs: Long) : Schedule()
    data class Cron(val expression: String) : Schedule()
    data class Fixed(val times: List<Long>) : Schedule()
}

interface ScheduleListener {
    fun onScheduleExecuted(taskId: String, executionTime: Long)
    fun onScheduleFailed(taskId: String, error: String)
}

定时任务更关注“在什么时候执行”这一维度:

  • ScheduledTaskManager 用于管理周期性或基于 Cron/固定时间点的任务计划;
  • Schedule 使用 IntervalCronFixed 三种形式抽象不同类型的时间计划;
  • ScheduleListener 让你能够在任务触发时收到回调,记录执行时间或处理失败原因。

在实际设计中,后台任务和定时任务可以配合使用:定时任务负责触发时机,后台任务负责具体的工作内容和约束/重试逻辑。

第二部分:平台特定实现

Android 后台任务

// androidMain/kotlin/com/example/task/BackgroundTaskManager.kt
import androidx.work.*
import android.content.Context
import java.util.concurrent.TimeUnit

actual class BackgroundTaskManager(private val context: Context) {
    private val workManager = WorkManager.getInstance(context)
    private val listeners = mutableListOf<TaskListener>()
    
    actual suspend fun scheduleTask(task: BackgroundTask): String {
        return try {
            val workRequest = when (task.type) {
                TaskType.ONE_TIME -> {
                    OneTimeWorkRequestBuilder<BackgroundWorker>()
                        .setInputData(workDataOf("taskId" to task.id))
                        .setBackoffCriteria(
                            when (task.retryPolicy.backoffPolicy) {
                                BackoffPolicy.LINEAR -> BackoffPolicy.LINEAR
                                BackoffPolicy.EXPONENTIAL -> BackoffPolicy.EXPONENTIAL
                            },
                            task.retryPolicy.initialDelay,
                            TimeUnit.MILLISECONDS
                        )
                        .build()
                }
                TaskType.PERIODIC -> {
                    PeriodicWorkRequestBuilder<BackgroundWorker>(
                        15, TimeUnit.MINUTES
                    ).setInputData(workDataOf("taskId" to task.id))
                        .build()
                }
                TaskType.DELAYED -> {
                    OneTimeWorkRequestBuilder<BackgroundWorker>()
                        .setInitialDelay(task.timeout, TimeUnit.MILLISECONDS)
                        .setInputData(workDataOf("taskId" to task.id))
                        .build()
                }
            }
            
            workManager.enqueueUniqueWork(
                task.id,
                ExistingWorkPolicy.KEEP,
                workRequest
            )
            
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onTaskFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
    
    actual suspend fun cancelTask(taskId: String): Boolean {
        return try {
            workManager.cancelUniqueWork(taskId)
            listeners.forEach { it.onTaskCancelled(taskId) }
            true
        } catch (e: Exception) {
            false
        }
    }
    
    actual suspend fun getTaskStatus(taskId: String): TaskStatus? {
        return try {
            val workInfo = workManager.getWorkInfosForUniqueWork(taskId).await()
            workInfo.firstOrNull()?.state?.let { state ->
                when (state) {
                    WorkInfo.State.ENQUEUED -> TaskStatus.PENDING
                    WorkInfo.State.RUNNING -> TaskStatus.RUNNING
                    WorkInfo.State.SUCCEEDED -> TaskStatus.COMPLETED
                    WorkInfo.State.FAILED -> TaskStatus.FAILED
                    WorkInfo.State.CANCELLED -> TaskStatus.CANCELLED
                    WorkInfo.State.BLOCKED -> TaskStatus.PENDING
                }
            }
        } catch (e: Exception) {
            null
        }
    }
}

// Android 定时任务
actual class ScheduledTaskManager(private val context: Context) {
    private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as android.app.AlarmManager
    private val listeners = mutableListOf<ScheduleListener>()
    
    actual suspend fun scheduleRepeating(task: ScheduledTask): String {
        return try {
            when (val schedule = task.schedule) {
                is Schedule.Interval -> {
                    val pendingIntent = createPendingIntent(task.id)
                    alarmManager.setRepeating(
                        android.app.AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis() + schedule.intervalMs,
                        schedule.intervalMs,
                        pendingIntent
                    )
                }
                else -> {}
            }
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onScheduleFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
    
    private fun createPendingIntent(taskId: String): android.app.PendingIntent {
        val intent = android.content.Intent(context, ScheduleReceiver::class.java)
        intent.putExtra("taskId", taskId)
        return android.app.PendingIntent.getBroadcast(
            context,
            taskId.hashCode(),
            intent,
            android.app.PendingIntent.FLAG_UPDATE_CURRENT
        )
    }
}

Android 端主要基于:

  • WorkManager 实现可靠的后台任务调度,支持网络、电量、充电等约束,并自动处理任务重试;
  • AlarmManager 实现定时触发,适合需要在特定时间点唤醒应用执行逻辑的场景。

示例中展示了如何:

  • BackgroundTask 转换为对应的 WorkRequest,配置退避策略和唯一任务 ID;
  • 使用 getWorkInfosForUniqueWork 查询任务状态,并映射到通用的 TaskStatus
  • 通过 AlarmManager.setRepeating 实现简单的间隔型定时任务。

在工程实践中,需要进一步关注:

  • 不同 Android 版本上的后台限制(Doze 模式、后台执行限制等);
  • 使用 WorkManager 统一替代部分不可靠的手写定时逻辑;
  • 对关键任务增加日志和监控,避免静默失败。

iOS 后台任务

// iosMain/kotlin/com/example/task/BackgroundTaskManager.kt
import platform.UIKit.*
import platform.Foundation.*

actual class BackgroundTaskManager {
    private val listeners = mutableListOf<TaskListener>()
    private val backgroundTasks = mutableMapOf<String, UIBackgroundTaskIdentifier>()
    
    actual suspend fun scheduleTask(task: BackgroundTask): String {
        return try {
            val taskId = UIApplication.sharedApplication.beginBackgroundTaskWithExpirationHandler {
                listeners.forEach { it.onTaskFailed(task.id, "Background task expired") }
            }
            
            backgroundTasks[task.id] = taskId
            listeners.forEach { it.onTaskStarted(task.id) }
            
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onTaskFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
    
    actual suspend fun cancelTask(taskId: String): Boolean {
        return try {
            backgroundTasks[taskId]?.let { bgTaskId ->
                UIApplication.sharedApplication.endBackgroundTask(bgTaskId)
                backgroundTasks.remove(taskId)
            }
            listeners.forEach { it.onTaskCancelled(taskId) }
            true
        } catch (e: Exception) {
            false
        }
    }
}

// iOS 定时任务
actual class ScheduledTaskManager {
    private val listeners = mutableListOf<ScheduleListener>()
    
    actual suspend fun scheduleRepeating(task: ScheduledTask): String {
        return try {
            when (val schedule = task.schedule) {
                is Schedule.Interval -> {
                    // Use Timer for interval-based scheduling
                    val timer = NSTimer.scheduledTimerWithTimeInterval(
                        schedule.intervalMs / 1000.0,
                        repeats = true
                    ) {
                        listeners.forEach { it.onScheduleExecuted(task.id, System.currentTimeMillis()) }
                    }
                }
                else -> {}
            }
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onScheduleFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
}

第三部分:编译后的代码形式

JavaScript 编译版本

// 后台任务管理器 (JavaScript/Web)
class BackgroundTaskManager {
    constructor() {
        this.listeners = [];
        this.tasks = new Map();
    }
    
    async scheduleTask(task) {
        try {
            if ('serviceWorker' in navigator) {
                const registration = await navigator.serviceWorker.ready;
                registration.sync.register(task.id);
                this.tasks.set(task.id, task);
                this.listeners.forEach(l => l.onTaskStarted(task.id));
                return task.id;
            }
        } catch (error) {
            this.listeners.forEach(l => l.onTaskFailed(task.id, error.message));
            return '';
        }
    }
    
    async cancelTask(taskId) {
        try {
            this.tasks.delete(taskId);
            this.listeners.forEach(l => l.onTaskCancelled(taskId));
            return true;
        } catch (error) {
            return false;
        }
    }
}

// 定时任务管理器
class ScheduledTaskManager {
    constructor() {
        this.listeners = [];
        this.timers = new Map();
    }
    
    async scheduleRepeating(task) {
        try {
            if (task.schedule.type === 'INTERVAL') {
                const timerId = setInterval(() => {
                    this.listeners.forEach(l => l.onScheduleExecuted(task.id, Date.now()));
                }, task.schedule.intervalMs);
                
                this.timers.set(task.id, timerId);
                return task.id;
            }
        } catch (error) {
            this.listeners.forEach(l => l.onScheduleFailed(task.id, error.message));
            return '';
        }
    }
}

第四部分:鸿蒙系统实际应用

鸿蒙后台任务

// ohos/kotlin/com/example/task/BackgroundTaskManager.kt
import ohos.app.ability.Ability
import ohos.bundle.ElementName
import ohos.app.ability.AbilityManager
import ohos.rpc.IRemoteObject

actual class BackgroundTaskManager(private val context: Ability) {
    private val listeners = mutableListOf<TaskListener>()
    
    actual suspend fun scheduleTask(task: BackgroundTask): String {
        return try {
            // Use HarmonyOS WorkScheduler
            val workInfo = ohos.app.ability.WorkSchedulerService.WorkInfo.Builder()
                .setWorkId(task.id.hashCode())
                .setWorkSchedulerType(ohos.app.ability.WorkSchedulerService.WorkInfo.WORK_TYPE_DELAYED)
                .setWantAgent(createWantAgent(task.id))
                .setDelay(task.timeout)
                .build()
            
            ohos.app.ability.WorkSchedulerService.scheduleWork(workInfo)
            listeners.forEach { it.onTaskStarted(task.id) }
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onTaskFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
    
    private fun createWantAgent(taskId: String): ohos.app.ability.wantAgent.WantAgent {
        // Create WantAgent for the task
        return ohos.app.ability.wantAgent.WantAgentHelper.getWantAgent(
            context,
            0,
            ohos.app.ability.wantAgent.WantAgent.FLAG_CANCEL_CURRENT
        )
    }
}

// 鸿蒙定时任务
actual class ScheduledTaskManager(private val context: Ability) {
    private val listeners = mutableListOf<ScheduleListener>()
    
    actual suspend fun scheduleRepeating(task: ScheduledTask): String {
        return try {
            when (val schedule = task.schedule) {
                is Schedule.Interval -> {
                    val workInfo = ohos.app.ability.WorkSchedulerService.WorkInfo.Builder()
                        .setWorkId(task.id.hashCode())
                        .setWorkSchedulerType(ohos.app.ability.WorkSchedulerService.WorkInfo.WORK_TYPE_PERIODIC)
                        .setInterval(schedule.intervalMs)
                        .build()
                    
                    ohos.app.ability.WorkSchedulerService.scheduleWork(workInfo)
                }
                else -> {}
            }
            task.id
        } catch (e: Exception) {
            listeners.forEach { it.onScheduleFailed(task.id, e.message ?: "Failed to schedule") }
            ""
        }
    }
}

第五部分:高级功能

任务链管理

// commonMain/kotlin/com/example/task/TaskChain.kt
class TaskChain(private val taskManager: BackgroundTaskManager) {
    private val tasks = mutableListOf<BackgroundTask>()
    
    fun addTask(task: BackgroundTask) = apply {
        tasks.add(task)
    }
    
    suspend fun execute(): Boolean {
        return try {
            for (task in tasks) {
                taskManager.scheduleTask(task)
            }
            true
        } catch (e: Exception) {
            false
        }
    }
}

// 条件任务执行
class ConditionalTaskExecutor(private val taskManager: BackgroundTaskManager) {
    suspend fun executeIf(
        condition: suspend () -> Boolean,
        task: BackgroundTask
    ): Boolean {
        return if (condition()) {
            taskManager.scheduleTask(task)
            true
        } else {
            false
        }
    }
}

在这里插入图片描述

总结

后台任务与定时任务是现代应用实现长期运行操作的重要方式。通过 KMP 框架,我们可以在共享代码中定义统一的后台任务接口,同时在各平台上利用原生 API 实现高效的任务调度。鸿蒙系统提供了完整的工作调度 API,使得跨平台实现相对平顺。关键是要合理处理任务约束、重试策略和电池消耗。

在工程实践中,可以重点思考:

  • 哪些任务必须在后台执行,哪些可以延迟或前台完成?
  • 如何根据网络、电量、充电状态等动态调整任务策略?
  • 在多端场景下,后台限制差异会如何影响你设计的任务模型?

建议的实战步骤:

  • 先挑选一两个场景(如“离线数据定时同步”“日志批量上报”)用本篇结构落地;
  • 再逐步为这些任务补充重试、约束、任务链等高级功能;
  • 最终沉淀出一套适用于你项目的“后台任务规范”和工具库。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐