KMP 结构适配鸿蒙:数据同步与离线支持理论知识
本文介绍了在Kotlin Multiplatform(KMP)中实现跨平台数据同步与离线支持的方案。通过定义统一的SyncManager和OfflineManager接口,实现了数据同步、离线缓存和冲突解决的核心功能。系统支持增量同步、自动定时同步,并提供细粒度的事件监听。针对不同平台(Android/iOS/鸿蒙/Web)分别实现了具体的数据存储和网络调用逻辑,同时保持统一的业务语义。该方案显著
·
项目概述
数据同步与离线支持是现代应用的关键功能。在 KMP 框架下,我们需要实现跨平台的数据同步机制、离线缓存和冲突解决。本文详细介绍了如何在 Kotlin Multiplatform 中实现高效的数据同步系统,包括增量同步、冲突检测和鸿蒙系统特定的数据管理方式。
与“只在线应用”相比,具备同步与离线能力的应用可以在以下场景显著提升体验:
- 弱网/无网环境:如地铁、隧道、机舱内仍可正常浏览和操作,待网络恢复后自动同步;
- 多端使用:手机、平板、PC 等多终端之间保持数据一致;
- 高可靠业务:例如表单、工单、检查记录等,必须保证数据不丢失且最终一致。
本篇目标是:
- 抽象统一的
SyncManager与OfflineManager接口,用于发起同步、管理离线数据; - 建模同步结果、同步状态、冲突信息与解决策略;
- 在 Android、iOS、鸿蒙和 Web 等平台上,用各自的数据能力实现同一套业务语义。
第一部分:数据同步核心概念
数据同步架构
// commonMain/kotlin/com/example/sync/SyncManager.kt
expect class SyncManager {
suspend fun syncData(dataType: String): SyncResult
suspend fun startAutoSync(interval: Long): Boolean
suspend fun stopAutoSync(): Boolean
suspend fun getLastSyncTime(dataType: String): Long
suspend fun getSyncStatus(): SyncStatus
fun addListener(listener: SyncListener)
}
data class SyncResult(
val success: Boolean,
val dataType: String,
val itemsSynced: Int,
val itemsFailed: Int,
val timestamp: Long,
val error: String? = null
)
enum class SyncStatus {
IDLE, SYNCING, PAUSED, ERROR
}
interface SyncListener {
fun onSyncStarted(dataType: String)
fun onSyncProgress(dataType: String, progress: Int, total: Int)
fun onSyncCompleted(result: SyncResult)
fun onSyncFailed(dataType: String, error: String)
}
// 离线支持
expect class OfflineManager {
suspend fun saveOfflineData(data: OfflineData): Boolean
suspend fun getOfflineData(key: String): OfflineData?
suspend fun getAllOfflineData(): List<OfflineData>
suspend fun clearOfflineData(): Boolean
suspend fun getOfflineDataSize(): Long
}
data class OfflineData(
val key: String,
val data: ByteArray,
val timestamp: Long,
val priority: Int = 0,
val retryCount: Int = 0
)
// 冲突解决
sealed class ConflictResolution {
object Local : ConflictResolution()
object Remote : ConflictResolution()
data class Merge(val mergeStrategy: (local: Any, remote: Any) -> Any) : ConflictResolution()
object Manual : ConflictResolution()
}
data class SyncConflict(
val key: String,
val localVersion: Any,
val remoteVersion: Any,
val timestamp: Long
)
这一部分定义了整个同步与离线系统的核心抽象:
SyncManager专注于“对某一数据类型进行同步”的流程,支持手动同步、自动定时同步、查询最后同步时间和当前状态等;SyncResult和SyncStatus用来描述同步结果与全局状态,便于在 UI 中进行展示和监控;SyncListener提供细粒度事件:开始、进度、完成、失败,方便做进度条、提示和日志;OfflineManager和OfflineData则负责离线数据的存储、查询与容量管理,是“断网期间先落地、后补偿”的基础;SyncConflict与ConflictResolution预留了对冲突检测和解决策略的扩展点。
在 KMP 结构中,可以在 common 层实现“业务级同步流程”(例如订单同步、配置同步),而在平台特定层完成具体的网络、存储、数据库调用。
第二部分:平台特定实现
Android 数据同步
// androidMain/kotlin/com/example/sync/SyncManager.kt
import android.content.Context
import android.content.SyncRequest
import android.accounts.Account
import android.content.ContentResolver
actual class SyncManager(private val context: Context) {
private val listeners = mutableListOf<SyncListener>()
private var autoSyncJob: Job? = null
actual suspend fun syncData(dataType: String): SyncResult {
return try {
listeners.forEach { it.onSyncStarted(dataType) }
val account = Account("default", "com.example.account")
val bundle = android.os.Bundle().apply {
putString("dataType", dataType)
}
ContentResolver.requestSync(account, "com.example.provider", bundle)
SyncResult(
success = true,
dataType = dataType,
itemsSynced = 0,
itemsFailed = 0,
timestamp = System.currentTimeMillis()
)
} catch (e: Exception) {
SyncResult(
success = false,
dataType = dataType,
itemsSynced = 0,
itemsFailed = 0,
timestamp = System.currentTimeMillis(),
error = e.message
)
}
}
actual suspend fun startAutoSync(interval: Long): Boolean {
return try {
autoSyncJob = GlobalScope.launch {
while (isActive) {
delay(interval)
syncData("all")
}
}
true
} catch (e: Exception) {
false
}
}
actual suspend fun stopAutoSync(): Boolean {
return try {
autoSyncJob?.cancel()
true
} catch (e: Exception) {
false
}
}
}
// Android 离线支持
actual class OfflineManager(private val context: Context) {
private val database = OfflineDatabase.getInstance(context)
actual suspend fun saveOfflineData(data: OfflineData): Boolean {
return try {
database.offlineDataDao().insert(data.toEntity())
true
} catch (e: Exception) {
false
}
}
actual suspend fun getOfflineData(key: String): OfflineData? {
return try {
database.offlineDataDao().getByKey(key)?.toOfflineData()
} catch (e: Exception) {
null
}
}
actual suspend fun getAllOfflineData(): List<OfflineData> {
return try {
database.offlineDataDao().getAll().map { it.toOfflineData() }
} catch (e: Exception) {
emptyList()
}
}
}
Android 端示例中:
SyncManager使用ContentResolver.requestSync与系统同步框架集成,适合需要与系统账号/Provider 深度集成的场景;startAutoSync通过协程定时触发syncData,演示了简单的自动同步机制;OfflineManager通过本地数据库(如 Room)持久化离线数据,并提供按 key/全部查询等接口。
实际工程中,常见做法是:
- 在应用层使用自建的同步 API + 本地数据库,而非强绑定系统的 SyncAdapter;
- 为每种数据类型设计好“变更记录表”,支持增量同步;
- 对离线队列进行容量控制和重试策略设计。
iOS 数据同步
// iosMain/kotlin/com/example/sync/SyncManager.kt
import platform.CloudKit.*
import platform.Foundation.*
actual class SyncManager {
private val listeners = mutableListOf<SyncListener>()
private val container = CKContainer.defaultContainer()
actual suspend fun syncData(dataType: String): SyncResult {
return try {
listeners.forEach { it.onSyncStarted(dataType) }
val database = container.privateCloudDatabase
val query = CKQuery(recordType = dataType, predicate = NSPredicate(value = true))
var itemsSynced = 0
var itemsFailed = 0
database.performQuery(query, inZoneWithID = null) { records, error ->
if (error == null) {
itemsSynced = records?.size ?: 0
} else {
itemsFailed = 1
}
}
SyncResult(
success = itemsFailed == 0,
dataType = dataType,
itemsSynced = itemsSynced,
itemsFailed = itemsFailed,
timestamp = System.currentTimeMillis()
)
} catch (e: Exception) {
SyncResult(
success = false,
dataType = dataType,
itemsSynced = 0,
itemsFailed = 0,
timestamp = System.currentTimeMillis(),
error = e.message
)
}
}
}
// iOS 离线支持
actual class OfflineManager {
private val fileManager = NSFileManager.defaultManager
actual suspend fun saveOfflineData(data: OfflineData): Boolean {
return try {
val documentsPath = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
true
).firstOrNull() as? String ?: return false
val filePath = "$documentsPath/${data.key}"
fileManager.createFileAtPath(filePath, contents = NSData(data.data), attributes = null)
true
} catch (e: Exception) {
false
}
}
}
iOS 端示例基于 CloudKit 和文件系统:
- 通过
CKContainer与 iCloud 数据库进行数据查询/同步,适合需要与苹果生态深度整合的应用; - 使用
NSFileManager将离线数据以文件形式存储在 Documents 目录中,结构简单、易于调试。
在实际项目中,也可以选择:
- 使用自建后端 + URLSession/数据库,替代 CloudKit;
- 将离线数据持久化到 SQLite/CoreData,并在 KMP 侧只暴露抽象接口。
第三部分:编译后的代码形式
JavaScript 编译版本
// 数据同步管理器 (JavaScript/Web)
class SyncManager {
constructor() {
this.listeners = [];
this.lastSyncTime = {};
}
async syncData(dataType) {
try {
this.listeners.forEach(l => l.onSyncStarted(dataType));
const response = await fetch(`/api/sync/${dataType}`);
const data = await response.json();
const result = {
success: response.ok,
dataType: dataType,
itemsSynced: data.items?.length || 0,
itemsFailed: 0,
timestamp: Date.now()
};
this.lastSyncTime[dataType] = Date.now();
this.listeners.forEach(l => l.onSyncCompleted(result));
return result;
} catch (error) {
this.listeners.forEach(l => l.onSyncFailed(dataType, error.message));
return {
success: false,
dataType: dataType,
itemsSynced: 0,
itemsFailed: 0,
timestamp: Date.now(),
error: error.message
};
}
}
async startAutoSync(interval) {
setInterval(() => {
this.syncData('all');
}, interval);
return true;
}
}
// 离线管理器
class OfflineManager {
constructor() {
this.db = null;
}
async saveOfflineData(data) {
try {
if ('indexedDB' in window) {
const request = indexedDB.open('OfflineDB', 1);
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(['offlineData'], 'readwrite');
const store = transaction.objectStore('offlineData');
store.put(data);
};
}
return true;
} catch (error) {
return false;
}
}
}
第四部分:鸿蒙系统实际应用
鸿蒙数据同步
// ohos/kotlin/com/example/sync/SyncManager.kt
import ohos.data.dataability.DataAbilityUtils
import ohos.app.Context
actual class SyncManager(private val context: Context) {
private val listeners = mutableListOf<SyncListener>()
actual suspend fun syncData(dataType: String): SyncResult {
return try {
listeners.forEach { it.onSyncStarted(dataType) }
// Use HarmonyOS DataAbility for sync
val uri = ohos.utils.net.Uri.parse("dataability://com.example.provider/$dataType")
val result = context.getContentResolver().query(uri, null, null, null, null)
val itemsSynced = result?.rowCount ?: 0
SyncResult(
success = true,
dataType = dataType,
itemsSynced = itemsSynced,
itemsFailed = 0,
timestamp = System.currentTimeMillis()
)
} catch (e: Exception) {
SyncResult(
success = false,
dataType = dataType,
itemsSynced = 0,
itemsFailed = 0,
timestamp = System.currentTimeMillis(),
error = e.message
)
}
}
}
// 鸿蒙离线支持
actual class OfflineManager(private val context: Context) {
private val database = OfflineDatabase.getInstance(context)
actual suspend fun saveOfflineData(data: OfflineData): Boolean {
return try {
database.offlineDataDao().insert(data)
true
} catch (e: Exception) {
false
}
}
}
第五部分:高级功能
冲突检测与解决
// commonMain/kotlin/com/example/sync/ConflictResolver.kt
class ConflictResolver {
suspend fun resolveConflict(
conflict: SyncConflict,
resolution: ConflictResolution
): Any {
return when (resolution) {
ConflictResolution.Local -> conflict.localVersion
ConflictResolution.Remote -> conflict.remoteVersion
is ConflictResolution.Merge -> {
resolution.mergeStrategy(conflict.localVersion, conflict.remoteVersion)
}
ConflictResolution.Manual -> conflict.localVersion // Default to local
}
}
}
// 增量同步
class IncrementalSync(private val syncManager: SyncManager) {
suspend fun syncIncremental(dataType: String, lastSyncTime: Long): SyncResult {
// Only sync data modified after lastSyncTime
return syncManager.syncData(dataType)
}
}

总结
数据同步与离线支持是现代应用的关键功能。通过 KMP 框架,我们可以在共享代码中定义统一的同步接口,同时在各平台上利用原生 API 实现高效的数据管理。鸿蒙系统提供了完整的数据管理 API,使得跨平台实现相对平顺。关键是要合理处理冲突、增量同步和离线缓存。
在工程实践中,可以重点思考:
- 你的业务到底需要“实时强一致”,还是“最终一致 + 可见提示”?
- 当前的同步流程是否支持增量,而不是每次全量覆盖?
- 离线数据的容量、过期策略和安全性是否有清晰的设计?
建议的实战路线:
- 先为一个具体数据域(如“用户草稿”“待上传记录”)实现完整的离线 + 同步流程;
- 引入冲突检测与简单的解决策略(如以时间戳/版本号为准);
- 逐步推广到更多数据域,并通过监控和日志不断优化同步策略与用户体验。
更多推荐




所有评论(0)