KMP 结构适配鸿蒙:后台任务与定时任务理论知识
本文介绍了在Kotlin Multiplatform中实现跨平台后台任务系统的方案。通过抽象统一的BackgroundTaskManager和ScheduledTaskManager接口,封装任务类型、约束条件和重试策略等核心概念。系统支持数据同步、定时提醒等典型场景,并在Android平台利用WorkManager实现具体调度。文章详细展示了后台任务架构设计,包括任务优先级、执行约束、重试机制等

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
项目概述
后台任务与定时任务是现代应用实现长期运行操作的重要方式。在 KMP 框架下,我们需要实现跨平台的后台任务调度、定时任务执行和唤醒锁管理。本文详细介绍了如何在 Kotlin Multiplatform 中实现高效的后台任务系统,包括 WorkManager、定时器和鸿蒙系统特定的后台任务处理方式。
典型场景包括:
- 数据同步与日志上报:在后台定期同步本地数据、上报埋点和崩溃信息;
- 定时提醒与推送预处理:在特定时间点触发任务,如日程提醒、订阅更新;
- 长时间运行任务:如音视频转码、大文件上传/下载、缓存预热等。
本篇的目标是:
- 在
commonMain中抽象统一的BackgroundTaskManager、ScheduledTaskManager接口; - 封装任务的类型、约束、重试策略和状态回调;
- 在 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使用Interval、Cron、Fixed三种形式抽象不同类型的时间计划;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,使得跨平台实现相对平顺。关键是要合理处理任务约束、重试策略和电池消耗。
在工程实践中,可以重点思考:
- 哪些任务必须在后台执行,哪些可以延迟或前台完成?
- 如何根据网络、电量、充电状态等动态调整任务策略?
- 在多端场景下,后台限制差异会如何影响你设计的任务模型?
建议的实战步骤:
- 先挑选一两个场景(如“离线数据定时同步”“日志批量上报”)用本篇结构落地;
- 再逐步为这些任务补充重试、约束、任务链等高级功能;
- 最终沉淀出一套适用于你项目的“后台任务规范”和工具库。
更多推荐
所有评论(0)