7 第6章 分布式数据管理
HarmonyOS应用开发者高级认证教程
8 第6章 分布式数据管理
8.1 学习目标
本章将深入讲解HarmonyOS分布式数据管理的核心技术,涵盖分布式数据对象、数据同步机制、关系型数据库高级用法、键值对分布式存储、首选项与持久化以及数据共享与交换。通过本章学习,读者将能够:
- 设计分布式数据对象模型并实现跨设备数据同步
- 掌握不同同步模式的适用场景与数据一致性保证机制
- 深入使用关系型数据库(RDB)进行复杂查询与性能优化
- 运用分布式KV存储实现多设备数据自动同步
- 掌握Preferences的高级用法与数据迁移策略
- 实现跨应用数据共享与DataShare服务开发
8.2 6.1 分布式数据对象
8.2.1 6.1.1 数据对象模型设计
分布式数据对象(Distributed Data Object)是HarmonyOS提供的跨设备数据共享机制,允许开发者像操作本地对象一样操作分布式数据。
数据对象模型设计原则:
- 原子性:每个数据对象应代表一个完整的业务实体
- 可序列化:对象属性必须是可序列化的基本类型或嵌套对象
- 版本控制:设计时考虑数据结构的向后兼容
- 冲突策略:预设属性级别的冲突解决策略
import { distributedObject } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// 数据对象模型 - 协同文档
class CollaborativeDocument {
// 基础属性
docId: string = ''
title: string = ''
content: string = ''
version: number = 0
// 元数据
creatorId: string = ''
creatorName: string = ''
createdAt: number = 0
updatedAt: number = 0
// 协同信息
collaborators: string[] = []
lastModifiedBy: string = ''
// 状态标记
isDirty: boolean = false
syncStatus: 'synced' | 'pending' | 'conflict' = 'synced'
}
// 数据对象管理器
class DistributedObjectManager {
private session: distributedObject.Session | null = null
private context: common.UIAbilityContext
private sessionId: string
constructor(context: common.UIAbilityContext, sessionId: string) {
this.context = context
this.sessionId = sessionId
}
// 初始化分布式数据对象会话
async initialize(): Promise<void> {
try {
// 创建分布式数据对象会话
this.session = distributedObject.create({
context: this.context,
sessionId: this.sessionId
})
// 初始化数据对象属性
this.initializeProperties()
// 注册变更监听
this.registerChangeListeners()
// 注册设备状态监听
this.registerStatusListeners()
console.info('分布式数据对象会话初始化成功')
} catch (error) {
console.error(`初始化失败: ${JSON.stringify(error)}`)
throw error
}
}
private initializeProperties(): void {
if (!this.session) return
// 设置初始属性值
const doc = new CollaborativeDocument()
doc.docId = `doc_${Date.now()}`
doc.title = '新文档'
doc.content = ''
doc.version = 1
doc.createdAt = Date.now()
this.session['docId'] = doc.docId
this.session['title'] = doc.title
this.session['content'] = doc.content
this.session['version'] = doc.version
this.session['createdAt'] = doc.createdAt
this.session['updatedAt'] = doc.createdAt
this.session['collaborators'] = JSON.stringify([])
this.session['syncStatus'] = doc.syncStatus
}
// 注册属性变更监听
private registerChangeListeners(): void {
if (!this.session) return
this.session.on('change', (
prop: string,
value: distributedObject.ObjectValue): void => {
console.info(`属性变更: ${prop} = ${JSON.stringify(value)}`)
// 根据属性类型执行不同的处理逻辑
switch (prop) {
case 'content':
this.handleContentChange(value as string)
break
case 'title':
this.handleTitleChange(value as string)
break
case 'version':
this.handleVersionChange(value as number)
break
default:
break
}
})
}
// 注册设备状态监听
private registerStatusListeners(): void {
if (!this.session) return
this.session.on('status', (
sessionId: string,
networkId: string,
status: number): void => {
// status: 0=设备加入, 1=设备退出, 2=设备在线
switch (status) {
case 0:
console.info(`设备加入会话: ${networkId}`)
break
case 1:
console.info(`设备离开会话: ${networkId}`)
break
case 2:
console.info(`设备在线: ${networkId}`)
break
}
})
}
// 更新文档内容
updateContent(newContent: string): void {
if (!this.session) return
this.session['content'] = newContent
this.session['updatedAt'] = Date.now()
this.session['version'] = (this.session['version'] as number) + 1
}
// 添加协作者
addCollaborator(userId: string): void {
if (!this.session) return
const collaborators = JSON.parse(
(this.session['collaborators'] as string) ?? '[]'
) as string[]
if (!collaborators.includes(userId)) {
collaborators.push(userId)
this.session['collaborators'] = JSON.stringify(collaborators)
}
}
// 保存会话数据到本地
async saveLocal(): Promise<void> {
if (!this.session) return
return new Promise((resolve, reject) => {
this.session!.save((error: Error) => {
if (error) {
console.error(`保存失败: ${JSON.stringify(error)}`)
reject(error)
} else {
console.info('会话数据已保存到本地')
resolve()
}
})
})
}
// 释放会话
release(): void {
if (this.session) {
this.session.off('change')
this.session.off('status')
this.session = null
}
}
private handleContentChange(content: string): void {
console.info(`文档内容已更新, 长度: ${content.length}`)
}
private handleTitleChange(title: string): void {
console.info(`文档标题已更新: ${title}`)
}
private handleVersionChange(version: number): void {
console.info(`文档版本更新: v${version}`)
}
}8.2.2 6.1.2 对象变更通知机制
分布式数据对象的变更通知机制基于观察者模式,支持属性级别的细粒度监听:
// 高级变更通知管理
class ChangeNotificationManager {
private changeHistory: ChangeRecord[] = []
private maxHistorySize: number = 100
private batchTimerId: number = -1
private pendingChanges: Map<string, Object> = new Map()
private batchInterval: number = 100 // 批量通知间隔(ms)
// 注册细粒度属性监听
registerPropertyWatch(
session: distributedObject.Session,
property: string,
callback: (oldValue: Object, newValue: Object) => void): void {
let lastValue = session[property]
session.on('change', (prop: string,
value: distributedObject.ObjectValue): void => {
if (prop === property) {
const oldValue = lastValue
lastValue = value
callback(oldValue, value)
// 记录变更历史
this.recordChange(property, oldValue, value)
}
})
}
// 批量变更通知
registerBatchWatch(
session: distributedObject.Session,
properties: string[],
callback: (changes: Map<string, { old: Object, new: Object }>) => void): void {
const pendingChanges = new Map<string, { old: Object, new: Object }>()
session.on('change', (prop: string,
value: distributedObject.ObjectValue): void => {
if (properties.includes(prop)) {
const oldValue = session[prop]
pendingChanges.set(prop, { old: oldValue, new: value })
// 清除之前的定时器,重新计时
if (this.batchTimerId !== -1) {
clearTimeout(this.batchTimerId)
}
// 设置新的批量通知定时器
this.batchTimerId = setTimeout(() => {
callback(new Map(pendingChanges))
pendingChanges.clear()
this.batchTimerId = -1
}, this.batchInterval)
}
})
}
// 记录变更历史
private recordChange(property: string,
oldValue: Object, newValue: Object): void {
const record: ChangeRecord = {
property,
oldValue: JSON.stringify(oldValue),
newValue: JSON.stringify(newValue),
timestamp: Date.now()
}
this.changeHistory.push(record)
// 限制历史记录大小
if (this.changeHistory.length > this.maxHistorySize) {
this.changeHistory.shift()
}
}
// 获取变更历史
getChangeHistory(property?: string): ChangeRecord[] {
if (property) {
return this.changeHistory.filter(r => r.property === property)
}
return [...this.changeHistory]
}
// 撤销最近的变更
undo(session: distributedObject.Session): boolean {
if (this.changeHistory.length === 0) return false
const lastChange = this.changeHistory.pop()!
session[lastChange.property] = JSON.parse(lastChange.oldValue)
return true
}
}
interface ChangeRecord {
property: string
oldValue: string
newValue: string
timestamp: number
}8.2.3 6.1.3 跨设备数据同步策略
// 数据同步策略管理器
class SyncStrategyManager {
private currentStrategy: SyncStrategy = SyncStrategy.REALTIME
private conflictResolver: ConflictResolver = new TimestampConflictResolver()
// 设置同步策略
setStrategy(strategy: SyncStrategy): void {
this.currentStrategy = strategy
console.info(`同步策略已切换为: ${strategy}`)
}
// 实时同步模式
async enableRealtimeSync(session: distributedObject.Session): Promise<void> {
// 实时同步:属性变更立即同步到所有设备
session.on('change', (prop: string,
value: distributedObject.ObjectValue): void => {
// 实时模式下,变更自动同步
console.info(`实时同步: ${prop} 已同步`)
})
}
// 定时同步模式
setupPeriodicSync(session: distributedObject.Session,
intervalMs: number): number {
return setInterval(async () => {
try {
await session.save()
console.info('定时同步完成')
} catch (error) {
console.error(`定时同步失败: ${JSON.stringify(error)}`)
}
}, intervalMs)
}
// 手动同步模式
async manualSync(session: distributedObject.Session): Promise<void> {
try {
await session.save()
console.info('手动同步完成')
} catch (error) {
console.error(`手动同步失败: ${JSON.stringify(error)}`)
throw error
}
}
// 设置冲突解决器
setConflictResolver(resolver: ConflictResolver): void {
this.conflictResolver = resolver
}
}
enum SyncStrategy {
REALTIME = 'realtime', // 实时同步
PERIODIC = 'periodic', // 定时同步
MANUAL = 'manual' // 手动同步
}
// 冲突解决器接口
interface ConflictResolver {
resolve(localValue: Object, remoteValue: Object,
property: string): Object
}
// 基于时间戳的冲突解决器
class TimestampConflictResolver implements ConflictResolver {
resolve(localValue: Object, remoteValue: Object,
property: string): Object {
// 简单策略:后写入的覆盖先写入的
if (property === 'updatedAt') {
const localTime = localValue as number
const remoteTime = remoteValue as number
return localTime > remoteTime ? localValue : remoteValue
}
// 默认采用远程值
return remoteValue
}
}
// 基于版本的冲突解决器
class VersionConflictResolver implements ConflictResolver {
resolve(localValue: Object, remoteValue: Object,
property: string): Object {
// 版本号大的优先
if (typeof localValue === 'number' && typeof remoteValue === 'number') {
return Math.max(localValue, remoteValue)
}
return remoteValue
}
}8.2.4 6.1.4 冲突检测与解决
// 高级冲突检测与解决系统
class AdvancedConflictDetector {
private localVersion: number = 0
private remoteVersion: number = 0
private lastSyncVersion: number = 0
private conflictLog: ConflictLogEntry[] = []
// 检测冲突
detectConflict(localData: Map<string, Object>,
remoteData: Map<string, Object>): Conflict[] {
const conflicts: Conflict[] = []
// 检查本地修改是否与远程修改冲突
for (const [key, localValue] of localData) {
const remoteValue = remoteData.get(key)
if (remoteValue !== undefined &&
JSON.stringify(localValue) !== JSON.stringify(remoteValue)) {
// 双方都修改了同一属性
conflicts.push({
property: key,
localValue: localValue,
remoteValue: remoteValue,
type: ConflictType.MODIFY_MODIFY,
timestamp: Date.now()
})
}
}
// 检查删除冲突
for (const [key] of remoteData) {
if (!localData.has(key) && this.wasModifiedLocally(key)) {
conflicts.push({
property: key,
localValue: undefined,
remoteValue: remoteData.get(key),
type: ConflictType.DELETE_MODIFY,
timestamp: Date.now()
})
}
}
return conflicts
}
// 自动解决冲突
autoResolve(conflicts: Conflict[],
strategy: ConflictResolutionStrategy): Map<string, Object> {
const resolved = new Map<string, Object>()
for (const conflict of conflicts) {
let resolvedValue: Object
switch (strategy) {
case ConflictResolutionStrategy.LOCAL_WINS:
resolvedValue = conflict.localValue
break
case ConflictResolutionStrategy.REMOTE_WINS:
resolvedValue = conflict.remoteValue
break
case ConflictResolutionStrategy.TIMESTAMP:
// 基于时间戳选择最新的值
resolvedValue = conflict.remoteValue
break
case ConflictResolutionStrategy.MERGE:
// 尝试合并(仅适用于特定类型的数据)
resolvedValue = this.tryMerge(
conflict.localValue, conflict.remoteValue)
break
default:
resolvedValue = conflict.remoteValue
}
resolved.set(conflict.property, resolvedValue)
// 记录冲突解决日志
this.conflictLog.push({
conflict: conflict,
strategy: strategy,
resolvedValue: resolvedValue,
timestamp: Date.now()
})
}
return resolved
}
private tryMerge(local: Object, remote: Object): Object {
// 对于字符串类型,尝试拼接
if (typeof local === 'string' && typeof remote === 'string') {
return `${local}\n${remote}`
}
// 对于数组类型,尝试合并
if (Array.isArray(local) && Array.isArray(remote)) {
return [...new Set([...local, ...remote])]
}
// 默认返回远程值
return remote
}
private wasModifiedLocally(key: string): boolean {
return false
}
}
interface Conflict {
property: string
localValue: Object
remoteValue: Object
type: ConflictType
timestamp: number
}
enum ConflictType {
MODIFY_MODIFY, // 双方都修改
DELETE_MODIFY, // 一方删除,一方修改
INSERT_INSERT // 双方都插入
}
enum ConflictResolutionStrategy {
LOCAL_WINS,
REMOTE_WINS,
TIMESTAMP,
MERGE
}
interface ConflictLogEntry {
conflict: Conflict
strategy: ConflictResolutionStrategy
resolvedValue: Object
timestamp: number
}8.3 6.2 分布式数据同步
8.3.1 6.2.1 同步模式
HarmonyOS提供了三种数据同步模式,开发者需要根据业务场景选择合适的模式:
import { distributedKVStore } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// 同步模式管理器
class SyncModeManager {
private kvStore: distributedKVStore.SingleKVStore | null = null
private currentMode: SyncMode = SyncMode.MANUAL
async initialize(context: common.UIAbilityContext): Promise<void> {
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: 'com.example.distributeddata'
}
const kvManager = distributedKVStore.createKVManager(kvManagerConfig)
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: false, // 手动控制同步
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
}
this.kvStore = await kvManager.getKVStore<distributedKVStore.SingleKVStore>(
'sync_demo_store', options
)
}
// 设置同步模式
setSyncMode(mode: SyncMode): void {
this.currentMode = mode
console.info(`同步模式切换为: ${mode}`)
}
// 实时同步 - 数据变更立即同步
async enableRealtimeSync(): Promise<void> {
if (!this.kvStore) return
// 配置自动同步
const syncOptions: distributedKVStore.SyncOptions = {
syncMode: distributedKVStore.SyncMode.PUSH_PULL,
syncInterval: 0 // 立即同步
}
// 注册设备列表变更监听
this.kvStore.on('syncStatusChange', (
syncState: distributedKVStore.SyncState): void => {
console.info(`同步状态变更: ${syncState}`)
})
console.info('实时同步已启用')
}
// 定时同步 - 按固定间隔同步
async enablePeriodicSync(intervalMs: number): Promise<number> {
if (!this.kvStore) return -1
const timerId = setInterval(async () => {
try {
await this.performSync()
console.info(`定时同步完成, 间隔: ${intervalMs}ms`)
} catch (error) {
console.error(`定时同步失败: ${JSON.stringify(error)}`)
}
}, intervalMs)
console.info(`定时同步已启用, 间隔: ${intervalMs}ms`)
return timerId
}
// 手动同步 - 按需触发
async performSync(): Promise<void> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
// 获取设备列表
const deviceIds = this.getConnectedDeviceIds()
if (deviceIds.length === 0) {
console.warn('没有可用的同步设备')
return
}
// 执行同步
return new Promise((resolve, reject) => {
this.kvStore!.sync(deviceIds, {
mode: distributedKVStore.SyncMode.PUSH_PULL,
delay: 0
}, (err: Error) => {
if (err) {
console.error(`同步失败: ${JSON.stringify(err)}`)
reject(err)
} else {
console.info('同步成功')
resolve()
}
})
})
}
private getConnectedDeviceIds(): string[] {
// 获取已连接的设备ID列表
return []
}
}
enum SyncMode {
REALTIME = 'realtime',
PERIODIC = 'periodic',
MANUAL = 'manual'
}8.3.2 6.2.2 数据一致性保证
// 数据一致性管理器
class DataConsistencyManager {
private versionMap: Map<string, number> = new Map()
private pendingOperations: DataOperation[] = []
private isProcessing: boolean = false
// 写入数据(带版本号)
async writeWithVersion(key: string, value: Object,
expectedVersion: number): Promise<WriteResult> {
const currentVersion = this.versionMap.get(key) ?? 0
if (expectedVersion !== currentVersion) {
// 版本冲突
return {
success: false,
currentVersion: currentVersion,
message: `版本冲突: 期望 ${expectedVersion}, 实际 ${currentVersion}`
}
}
// 版本号递增
const newVersion = currentVersion + 1
this.versionMap.set(key, newVersion)
// 记录操作日志(用于冲突恢复)
this.pendingOperations.push({
type: 'write',
key: key,
value: JSON.stringify(value),
version: newVersion,
timestamp: Date.now()
})
return {
success: true,
currentVersion: newVersion,
message: '写入成功'
}
}
// 乐观锁机制
async optimisticUpdate(key: string,
updateFn: (currentValue: Object) => Object): Promise<boolean> {
let retries = 0
const maxRetries = 3
while (retries < maxRetries) {
const currentVersion = this.versionMap.get(key) ?? 0
const currentValue = this.readValue(key)
const newValue = updateFn(currentValue)
const result = await this.writeWithVersion(key, newValue, currentVersion)
if (result.success) {
return true
}
retries++
console.warn(`乐观锁冲突, 重试 ${retries}/${maxRetries}`)
}
return false
}
private readValue(key: string): Object {
return {}
}
}
interface WriteResult {
success: boolean
currentVersion: number
message: string
}
interface DataOperation {
type: 'write' | 'delete' | 'update'
key: string
value?: string
version: number
timestamp: number
}8.3.3 6.2.3 离线数据处理
// 离线数据处理器
class OfflineDataHandler {
private offlineQueue: OfflineOperation[] = []
private isOnline: boolean = true
private maxQueueSize: number = 1000
private storageKey: string = 'offline_queue'
// 网络状态感知
setOnlineStatus(online: boolean): void {
const wasOffline = !this.isOnline
this.isOnline = online
if (online && wasOffline) {
// 从离线恢复为在线,处理积压的操作
this.processOfflineQueue()
}
}
// 离线时缓存操作
async enqueueOperation(operation: OfflineOperation): Promise<void> {
if (this.offlineQueue.length >= this.maxQueueSize) {
// 队列已满,执行淘汰策略
this.evictOldestOperations()
}
operation.timestamp = Date.now()
operation.retryCount = 0
this.offlineQueue.push(operation)
// 持久化到本地存储
await this.persistQueue()
console.info(`操作已入队: ${operation.type} on ${operation.key}`)
}
// 处理离线队列
async processOfflineQueue(): Promise<void> {
if (this.isProcessing || this.offlineQueue.length === 0) return
this.isProcessing = true
console.info(`开始处理离线队列, 共 ${this.offlineQueue.length} 个操作`)
const failedOps: OfflineOperation[] = []
for (const op of this.offlineQueue) {
try {
await this.executeOperation(op)
console.info(`操作执行成功: ${op.type} on ${op.key}`)
} catch (error) {
op.retryCount++
if (op.retryCount < 3) {
failedOps.push(op)
console.warn(`操作执行失败, 将重试 (${op.retryCount}/3): ${op.key}`)
} else {
console.error(`操作执行失败, 已放弃: ${op.key}`)
// 可以通知用户处理失败的操作
}
}
}
// 保留失败的操作
this.offlineQueue = failedOps
await this.persistQueue()
this.isProcessing = false
console.info(`离线队列处理完成, 剩余 ${this.offlineQueue.length} 个操作`)
}
private async executeOperation(op: OfflineOperation): Promise<void> {
// 实际执行数据操作
switch (op.type) {
case 'put':
// 执行写入
break
case 'delete':
// 执行删除
break
case 'update':
// 执行更新
break
}
}
private async persistQueue(): Promise<void> {
// 将队列持久化到本地存储
}
private evictOldestOperations(): void {
// 淘汰最旧的操作
const removeCount = Math.floor(this.maxQueueSize * 0.1)
this.offlineQueue.splice(0, removeCount)
console.warn(`队列已满, 已淘汰 ${removeCount} 个最旧操作`)
}
// 获取离线队列状态
getQueueStatus(): OfflineQueueStatus {
return {
totalOperations: this.offlineQueue.length,
oldestTimestamp: this.offlineQueue.length > 0
? this.offlineQueue[0].timestamp : 0,
isProcessing: this.isProcessing,
isOnline: this.isOnline
}
}
}
interface OfflineOperation {
type: 'put' | 'delete' | 'update'
key: string
value?: string
timestamp: number
retryCount: number
}
interface OfflineQueueStatus {
totalOperations: number
oldestTimestamp: number
isProcessing: boolean
isOnline: boolean
}8.3.4 6.2.4 网络状态感知
import { connection } from '@kit.NetworkKit'
// 网络状态监听器
class NetworkAwarenessManager {
private netConnection: connection.NetConnection | null = null
private isNetworkAvailable: boolean = false
private networkType: string = 'unknown'
private listeners: NetworkStateListener[] = []
// 初始化网络监听
initialize(): void {
this.netConnection = connection.createNetConnection()
// 网络可用
this.netConnection.on('netAvailable', (netHandle: connection.NetHandle): void => {
this.isNetworkAvailable = true
console.info('网络已连接')
this.notifyListeners({ available: true, type: this.networkType })
})
// 网络状态变化
this.netConnection.on('netCapabilitiesChange', (
netHandle: connection.NetHandle,
netCap: connection.NetCapabilityInfo): void => {
if (netCap.netCap) {
const bearerTypes = netCap.netCap.bearerTypes
if (bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
this.networkType = 'wifi'
} else if (bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
this.networkType = 'cellular'
} else {
this.networkType = 'other'
}
}
console.info(`网络类型: ${this.networkType}`)
})
// 网络丢失
this.netConnection.on('netLost', (netHandle: connection.NetHandle): void => {
this.isNetworkAvailable = false
this.networkType = 'none'
console.info('网络已断开')
this.notifyListeners({ available: false, type: 'none' })
})
// 网络连接
this.netConnection.on('netConnectionPropertiesChange', (
netHandle: connection.NetHandle,
netConnectionProperties: connection.NetConnectionProperties): void => {
console.info('网络连接属性变更')
})
// 注册网络监听
this.netConnection.register((error) => {
if (error) {
console.error(`注册网络监听失败: ${JSON.stringify(error)}`)
}
})
}
// 添加网络状态监听器
addListener(listener: NetworkStateListener): void {
this.listeners.push(listener)
}
// 移除监听器
removeListener(listener: NetworkStateListener): void {
const index = this.listeners.indexOf(listener)
if (index >= 0) {
this.listeners.splice(index, 1)
}
}
// 获取当前网络状态
getNetworkStatus(): NetworkStatus {
return {
available: this.isNetworkAvailable,
type: this.networkType
}
}
// 根据网络状态决定同步策略
getSyncStrategyForNetwork(): SyncMode {
if (!this.isNetworkAvailable) {
return SyncMode.MANUAL // 离线模式
}
switch (this.networkType) {
case 'wifi':
return SyncMode.REALTIME // WiFi下实时同步
case 'cellular':
return SyncMode.PERIODIC // 蜂窝网络下定时同步,节省流量
default:
return SyncMode.MANUAL
}
}
private notifyListeners(status: NetworkStatus): void {
for (const listener of this.listeners) {
listener.onNetworkChange(status)
}
}
// 释放资源
destroy(): void {
if (this.netConnection) {
this.netConnection.unregister((error) => {
if (error) {
console.error(`注销网络监听失败: ${JSON.stringify(error)}`)
}
})
this.netConnection = null
}
}
}
interface NetworkStateListener {
onNetworkChange(status: NetworkStatus): void
}
interface NetworkStatus {
available: boolean
type: string
}
enum SyncMode {
REALTIME = 'realtime',
PERIODIC = 'periodic',
MANUAL = 'manual'
}8.4 6.3 关系型数据库高级
8.4.1 6.3.1 关系型数据库(RDB)深入
import { relationalStore } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// RDB高级操作管理器
class AdvancedRDBManager {
private rdbStore: relationalStore.RdbStore | null = null
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 初始化数据库
async initialize(dbName: string): Promise<void> {
const config: relationalStore.StoreConfig = {
name: dbName,
securityLevel: relationalStore.SecurityLevel.S1,
isVector: false
}
try {
this.rdbStore = await relationalStore.getRdbStore(this.context, config)
await this.createTables()
console.info('数据库初始化成功')
} catch (error) {
console.error(`数据库初始化失败: ${JSON.stringify(error)}`)
throw error
}
}
// 创建表结构
private async createTables(): Promise<void> {
if (!this.rdbStore) return
// 用户表
const createUsersSQL = `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER,
department_id INTEGER,
created_at TEXT DEFAULT (datetime('now', 'localtime')),
updated_at TEXT DEFAULT (datetime('now', 'localtime')),
is_active INTEGER DEFAULT 1,
FOREIGN KEY (department_id) REFERENCES departments(id)
)
`
// 部门表
const createDepartmentsSQL = `
CREATE TABLE IF NOT EXISTS departments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
manager_id INTEGER,
created_at TEXT DEFAULT (datetime('now', 'localtime'))
)
`
// 项目表
const createProjectsSQL = `
CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
status TEXT DEFAULT 'active',
start_date TEXT,
end_date TEXT,
budget REAL,
department_id INTEGER,
FOREIGN KEY (department_id) REFERENCES departments(id)
)
`
// 用户-项目关联表
const createUserProjectsSQL = `
CREATE TABLE IF NOT EXISTS user_projects (
user_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,
role TEXT DEFAULT 'member',
joined_at TEXT DEFAULT (datetime('now', 'localtime')),
PRIMARY KEY (user_id, project_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (project_id) REFERENCES projects(id)
)
`
await this.rdbStore.executeSql(createUsersSQL)
await this.rdbStore.executeSql(createDepartmentsSQL)
await this.rdbStore.executeSql(createProjectsSQL)
await this.rdbStore.executeSql(createUserProjectsSQL)
// 创建索引
await this.createIndexes()
}
// 创建索引
private async createIndexes(): Promise<void> {
if (!this.rdbStore) return
const indexSQLs = [
'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)',
'CREATE INDEX IF NOT EXISTS idx_users_department ON users(department_id)',
'CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status)',
'CREATE INDEX IF NOT EXISTS idx_projects_department ON projects(department_id)',
'CREATE INDEX IF NOT EXISTS idx_user_projects_user ON user_projects(user_id)',
'CREATE INDEX IF NOT EXISTS idx_user_projects_project ON user_projects(project_id)'
]
for (const sql of indexSQLs) {
await this.rdbStore.executeSql(sql)
}
}
// 高级查询 - 多表关联查询
async queryUsersWithDepartment(): Promise<relationalStore.ResultSet> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
const sql = `
SELECT
u.id, u.name, u.email, u.age,
d.name AS department_name,
u.created_at
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
WHERE u.is_active = 1
ORDER BY u.created_at DESC
LIMIT ? OFFSET ?
`
const predicates = new relationalStore.RdbPredicates('users')
return this.rdbStore.querySql(sql, ['20', '0'])
}
// 事务操作
async executeInTransaction(
operations: (store: relationalStore.RdbStore) => Promise<void>):
Promise<void> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
try {
// 开始事务
await this.rdbStore.beginTransaction()
// 执行操作
await operations(this.rdbStore)
// 提交事务
await this.rdbStore.commit()
console.info('事务提交成功')
} catch (error) {
// 回滚事务
if (this.rdbStore) {
await this.rdbStore.rollback()
}
console.error(`事务回滚: ${JSON.stringify(error)}`)
throw error
}
}
// 批量插入
async batchInsert(tableName: string,
data: relationalStore.ValuesBucket[]): Promise<number> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
let insertedCount = 0
await this.executeInTransaction(async (store) => {
for (const bucket of data) {
const rowId = await store.insert(tableName, bucket)
if (rowId !== -1) {
insertedCount++
}
}
})
console.info(`批量插入完成: ${insertedCount}/${data.length}`)
return insertedCount
}
// 关闭数据库
async close(): Promise<void> {
if (this.rdbStore) {
await relationalStore.deleteRdbStore(this.context, 'app.db')
this.rdbStore = null
}
}
}8.4.2 6.3.2 SQL查询优化
// SQL查询优化器
class QueryOptimizer {
private rdbStore: relationalStore.RdbStore | null = null
constructor(store: relationalStore.RdbStore) {
this.rdbStore = store
}
// 使用谓词查询代替SQL字符串(更安全、更高效)
async optimizedQueryWithPredicates(
departmentId: number,
minAge: number,
maxAge: number,
page: number,
pageSize: number): Promise<relationalStore.ResultSet> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
// 使用 RdbPredicates 构建查询
const predicates = new relationalStore.RdbPredicates('users')
// 条件过滤(谓词下推)
predicates.equalTo('department_id', departmentId.toString())
predicates.greaterThanOrEqualTo('age', minAge.toString())
predicates.lessThanOrEqualTo('age', maxAge.toString())
predicates.equalTo('is_active', 1)
// 排序
predicates.orderByDesc('created_at')
// 分页
predicates.limitAs(pageSize.toString())
predicates.offsetAs((page * pageSize).toString())
// 指定返回列(避免 SELECT *)
predicates.resultColumnsAs(['id', 'name', 'email', 'age', 'created_at'])
return this.rdbStore.query(predicates)
}
// 使用预编译语句(防止SQL注入,提高重复查询效率)
async queryWithPreparedStatement(
email: string): Promise<relationalStore.ResultSet> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
// 预编译SQL
const sql = 'SELECT * FROM users WHERE email = ?'
return this.rdbStore.querySql(sql, [email])
}
// 聚合查询优化
async getDepartmentStats(): Promise<DepartmentStats[]> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
const sql = `
SELECT
d.id,
d.name AS department_name,
COUNT(u.id) AS user_count,
AVG(u.age) AS avg_age,
MIN(u.age) AS min_age,
MAX(u.age) AS max_age
FROM departments d
LEFT JOIN users u ON d.id = u.department_id AND u.is_active = 1
GROUP BY d.id, d.name
HAVING COUNT(u.id) > 0
ORDER BY user_count DESC
`
const resultSet = this.rdbStore.querySqlSync(sql)
const stats: DepartmentStats[] = []
while (resultSet.goToNextRow()) {
stats.push({
departmentId: resultSet.getLong(0),
departmentName: resultSet.getString(1),
userCount: resultSet.getLong(2),
avgAge: resultSet.getDouble(3),
minAge: resultSet.getLong(4),
maxAge: resultSet.getLong(5)
})
}
resultSet.close()
return stats
}
}
interface DepartmentStats {
departmentId: number
departmentName: string
userCount: number
avgAge: number
minAge: number
maxAge: number
}8.4.3 6.3.3 谓词下推与索引优化
// 谓词下推与索引优化示例
class PredicatePushdownDemo {
private rdbStore: relationalStore.RdbStore | null = null
constructor(store: relationalStore.RdbStore) {
this.rdbStore = store
}
// 反模式:不使用索引的查询
async badQuery(): Promise<void> {
if (!this.rdbStore) return
// 对字段使用函数,导致索引失效
const badSQL = `
SELECT * FROM users
WHERE UPPER(name) = 'ZHANG SAN'
AND created_at LIKE '2024%'
`
// 这个查询无法使用索引,因为对name使用了UPPER函数
}
// 优化方案:使用索引友好的查询
async optimizedQuery(): Promise<void> {
if (!this.rdbStore) return
// 使用精确匹配,可以利用索引
const predicates = new relationalStore.RdbPredicates('users')
predicates.equalTo('name', '张三')
predicates.between('created_at', '2024-01-01', '2024-12-31')
this.rdbStore.query(predicates)
}
// 复合索引的使用
async compositeIndexQuery(): Promise<void> {
if (!this.rdbStore) return
// 创建复合索引
await this.rdbStore.executeSql(
'CREATE INDEX IF NOT EXISTS idx_dept_age_active ' +
'ON users(department_id, age, is_active)'
)
// 查询条件与复合索引匹配
const predicates = new relationalStore.RdbPredicates('users')
predicates.equalTo('department_id', '1')
predicates.greaterThanOrEqualTo('age', '25')
predicates.equalTo('is_active', 1)
// 这个查询可以充分利用复合索引
this.rdbStore.query(predicates)
}
// 覆盖索引查询(只查询索引包含的列)
async coveringIndexQuery(): Promise<void> {
if (!this.rdbStore) return
// 创建覆盖索引
await this.rdbStore.executeSql(
'CREATE INDEX IF NOT EXISTS idx_covering ' +
'ON users(department_id, name, email)'
)
// 只查询索引中包含的列,无需回表
const sql = `
SELECT department_id, name, email
FROM users
WHERE department_id = 1
`
this.rdbStore.querySql(sql)
}
}8.4.4 6.3.4 数据备份与恢复
// 数据库备份与恢复管理器
class DatabaseBackupManager {
private rdbStore: relationalStore.RdbStore | null = null
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext,
store: relationalStore.RdbStore) {
this.context = context
this.rdbStore = store
}
// 导出数据库为JSON
async exportToJSON(): Promise<string> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
const exportData: Record<string, Object[]> = {}
// 获取所有表名
const tables = ['users', 'departments', 'projects', 'user_projects']
for (const table of tables) {
const resultSet = this.rdbStore.querySqlSync(
`SELECT * FROM ${table}`
)
const rows: Object[] = []
const columnNames = resultSet.columnNames
while (resultSet.goToNextRow()) {
const row: Record<string, Object> = {}
for (let i = 0; i < columnNames.length; i++) {
const type = resultSet.getColumnType(i)
switch (type) {
case relationalStore.ColumnType.INTEGER:
row[columnNames[i]] = resultSet.getLong(i)
break
case relationalStore.ColumnType.FLOAT:
row[columnNames[i]] = resultSet.getDouble(i)
break
case relationalStore.ColumnType.STRING:
row[columnNames[i]] = resultSet.getString(i)
break
case relationalStore.ColumnType.BLOB:
row[columnNames[i]] = 'BLOB_DATA'
break
default:
row[columnNames[i]] = null
break
}
}
rows.push(row)
}
resultSet.close()
exportData[table] = rows
}
return JSON.stringify(exportData, null, 2)
}
// 从JSON恢复数据
async restoreFromJSON(jsonData: string): Promise<void> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
const data = JSON.parse(jsonData) as Record<string, Object[]>
await this.rdbStore.beginTransaction()
try {
// 按依赖顺序恢复:先恢复被引用的表
const tableOrder = ['departments', 'users', 'projects', 'user_projects']
for (const table of tableOrder) {
const rows = data[table]
if (!rows) continue
// 清空表
const predicates = new relationalStore.RdbPredicates(table)
await this.rdbStore.delete(predicates)
// 恢复数据
for (const row of rows) {
const bucket: relationalStore.ValuesBucket = row as relationalStore.ValuesBucket
await this.rdbStore.insert(table, bucket)
}
}
await this.rdbStore.commit()
console.info('数据恢复成功')
} catch (error) {
await this.rdbStore.rollback()
console.error(`数据恢复失败: ${JSON.stringify(error)}`)
throw error
}
}
// 增量备份
async incrementalBackup(lastBackupTime: number):
Promise<Record<string, Object[]>> {
if (!this.rdbStore) {
throw new Error('数据库未初始化')
}
const backupData: Record<string, Object[]> = {}
const tables = ['users', 'departments', 'projects']
const lastBackupStr = new Date(lastBackupTime).toISOString()
for (const table of tables) {
const resultSet = this.rdbStore.querySqlSync(
`SELECT * FROM ${table} WHERE updated_at > ?`,
[lastBackupStr]
)
const rows: Object[] = []
const columnNames = resultSet.columnNames
while (resultSet.goToNextRow()) {
const row: Record<string, Object> = {}
for (let i = 0; i < columnNames.length; i++) {
const type = resultSet.getColumnType(i)
switch (type) {
case relationalStore.ColumnType.INTEGER:
row[columnNames[i]] = resultSet.getLong(i)
break
case relationalStore.ColumnType.FLOAT:
row[columnNames[i]] = resultSet.getDouble(i)
break
case relationalStore.ColumnType.STRING:
row[columnNames[i]] = resultSet.getString(i)
break
default:
row[columnNames[i]] = null
break
}
}
rows.push(row)
}
resultSet.close()
backupData[table] = rows
}
return backupData
}
}8.5 6.4 键值对分布式存储
8.5.1 6.4.1 分布式KV存储架构
import { distributedKVStore } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// 分布式KV存储管理器
class DistributedKVManager {
private kvManager: distributedKVStore.KVManager | null = null
private kvStore: distributedKVStore.SingleKVStore | null = null
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 初始化KV存储
async initialize(storeId: string): Promise<void> {
try {
// 创建KV管理器
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: this.context,
bundleName: 'com.example.distributeddata'
}
this.kvManager = distributedKVStore.createKVManager(kvManagerConfig)
// 配置KV存储选项
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: true, // 启用加密
backup: true, // 允许备份
autoSync: true, // 自动同步
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
}
// 获取KV存储实例
this.kvStore = await this.kvManager.getKVStore<distributedKVStore.SingleKVStore>(
storeId, options
)
// 注册数据变更监听
this.registerDataChangeListeners()
console.info('分布式KV存储初始化成功')
} catch (error) {
console.error(`初始化失败: ${JSON.stringify(error)}`)
throw error
}
}
// 写入数据
async put(key: string, value: string | number | boolean | Uint8Array):
Promise<void> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
try {
await this.kvStore.put(key, value)
console.info(`数据写入成功: ${key}`)
} catch (error) {
console.error(`数据写入失败: ${JSON.stringify(error)}`)
throw error
}
}
// 批量写入
async batchPut(entries: Map<string, string>): Promise<void> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
try {
const putBatch: distributedKVStore.Entry[] = []
entries.forEach((value: string, key: string) => {
putBatch.push({ key: key, value: { type: distributedKVStore.ValueType.STRING, value: value } })
})
await this.kvStore.putBatch(putBatch)
console.info(`批量写入成功: ${entries.size} 条`)
} catch (error) {
console.error(`批量写入失败: ${JSON.stringify(error)}`)
throw error
}
}
// 读取数据
async get(key: string): Promise<string | undefined> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
try {
const value = await this.kvStore.get(key) as string
return value
} catch (error) {
console.error(`数据读取失败: ${JSON.stringify(error)}`)
return undefined
}
}
// 批量读取
async batchGet(keys: string[]): Promise<Map<string, string>> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
const result = new Map<string, string>()
try {
const entries = await this.kvStore.getBatch(keys)
for (const entry of entries) {
result.set(entry.key, entry.value.value as string)
}
} catch (error) {
console.error(`批量读取失败: ${JSON.stringify(error)}`)
}
return result
}
// 删除数据
async delete(key: string): Promise<void> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
try {
await this.kvStore.delete(key)
console.info(`数据删除成功: ${key}`)
} catch (error) {
console.error(`数据删除失败: ${JSON.stringify(error)}`)
throw error
}
}
// 注册数据变更监听
private registerDataChangeListeners(): void {
if (!this.kvStore) return
// 监听本地数据变更
this.kvStore.on('change', (data: distributedKVStore.ChangeNotification): void => {
// 处理插入事件
for (const entry of data.insertEntries) {
console.info(`数据插入: ${entry.key}`)
}
// 处理更新事件
for (const entry of data.updateEntries) {
console.info(`数据更新: ${entry.key}`)
}
// 处理删除事件
for (const entry of data.deleteEntries) {
console.info(`数据删除: ${entry.key}`)
}
})
// 监听同步状态变更
this.kvStore.on('syncStatusChange', (
syncState: distributedKVStore.SyncState): void => {
console.info(`同步状态: ${syncState}`)
})
}
// 手动触发同步
async sync(deviceIds: string[]): Promise<void> {
if (!this.kvStore) {
throw new Error('KV存储未初始化')
}
return new Promise((resolve, reject) => {
this.kvStore!.sync(deviceIds, {
mode: distributedKVStore.SyncMode.PUSH_PULL,
delay: 0
}, (err: Error) => {
if (err) reject(err)
else resolve()
})
})
}
// 释放资源
async destroy(): Promise<void> {
if (this.kvStore) {
this.kvStore.off('change')
this.kvStore.off('syncStatusChange')
}
if (this.kvManager) {
await this.kvManager.closeAllKVStores()
}
}
}8.5.2 6.4.2 数据分区策略
// KV数据分区管理器
class KVPartitionManager {
private kvManager: distributedKVStore.KVManager | null = null
private partitions: Map<string, distributedKVStore.SingleKVStore> =
new Map()
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
async initialize(): Promise<void> {
const config: distributedKVStore.KVManagerConfig = {
context: this.context,
bundleName: 'com.example.distributeddata'
}
this.kvManager = distributedKVStore.createKVManager(config)
}
// 创建数据分区
async createPartition(partitionName: string,
options: PartitionOptions): Promise<void> {
if (!this.kvManager) {
throw new Error('KV管理器未初始化')
}
const storeOptions: distributedKVStore.Options = {
createIfMissing: true,
encrypt: options.encrypted ?? false,
backup: options.backup ?? false,
autoSync: options.autoSync ?? true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: options.securityLevel ??
distributedKVStore.SecurityLevel.S1
}
const store = await this.kvManager.getKVStore<distributedKVStore.SingleKVStore>(
partitionName, storeOptions
)
this.partitions.set(partitionName, store)
console.info(`数据分区已创建: ${partitionName}`)
}
// 根据数据类型获取对应分区
getPartitionForType(dataType: DataType): distributedKVStore.SingleKVStore | null {
const partitionName = this.getPartitionName(dataType)
return this.partitions.get(partitionName) ?? null
}
private getPartitionName(dataType: DataType): string {
switch (dataType) {
case DataType.USER_SETTINGS:
return 'partition_settings'
case DataType.CACHE:
return 'partition_cache'
case DataType.SYNC_DATA:
return 'partition_sync'
case DataType.OFFLINE:
return 'partition_offline'
default:
return 'partition_default'
}
}
}
interface PartitionOptions {
encrypted?: boolean
backup?: boolean
autoSync?: boolean
securityLevel?: distributedKVStore.SecurityLevel
}
enum DataType {
USER_SETTINGS,
CACHE,
SYNC_DATA,
OFFLINE
}8.5.3 6.4.3 数据加密存储
// 加密KV存储管理器
class EncryptedKVManager {
private kvStore: distributedKVStore.SingleKVStore | null = null
private kvManager: distributedKVStore.KVManager | null = null
async initialize(context: common.UIAbilityContext): Promise<void> {
const config: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: 'com.example.distributeddata'
}
this.kvManager = distributedKVStore.createKVManager(config)
// 使用最高安全级别
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: true, // 启用加密
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S4 // 最高安全级别
}
this.kvStore = await this.kvManager.getKVStore<distributedKVStore.SingleKVStore>(
'encrypted_store', options
)
}
// 存储敏感数据
async storeSensitiveData(key: string, data: SensitiveData): Promise<void> {
if (!this.kvStore) {
throw new Error('加密存储未初始化')
}
// 序列化并加密存储
const serialized = JSON.stringify(data)
await this.kvStore.put(key, serialized)
console.info(`敏感数据已加密存储: ${key}`)
}
// 读取敏感数据
async retrieveSensitiveData<T extends SensitiveData>(
key: string): Promise<T | null> {
if (!this.kvStore) {
throw new Error('加密存储未初始化')
}
try {
const value = await this.kvStore.get(key) as string
return JSON.parse(value) as T
} catch (error) {
console.error(`读取敏感数据失败: ${JSON.stringify(error)}`)
return null
}
}
}
interface SensitiveData {
type: string
data: string
timestamp: number
expiresAt?: number
}8.6 6.5 首选项与持久化
8.6.1 6.5.1 Preferences异步操作
import { preferences } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// Preferences高级管理器
class AdvancedPreferencesManager {
private prefStore: preferences.Preferences | null = null
private context: common.UIAbilityContext
private prefName: string = 'app_preferences'
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 初始化Preferences
async initialize(): Promise<void> {
try {
this.prefStore = await preferences.getPreferences(
this.context, this.prefName
)
console.info('Preferences初始化成功')
} catch (error) {
console.error(`Preferences初始化失败: ${JSON.stringify(error)}`)
throw error
}
}
// 异步读取
async getString(key: string, defaultValue: string = ''): Promise<string> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
return await this.prefStore.get(key, defaultValue) as string
}
async getNumber(key: string, defaultValue: number = 0): Promise<number> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
return await this.prefStore.get(key, defaultValue) as number
}
async getBoolean(key: string, defaultValue: boolean = false):
Promise<boolean> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
return await this.prefStore.get(key, defaultValue) as boolean
}
// 异步写入
async putString(key: string, value: string): Promise<void> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
await this.prefStore.put(key, value)
await this.prefStore.flush()
}
async putNumber(key: string, value: number): Promise<void> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
await this.prefStore.put(key, value)
await this.prefStore.flush()
}
// 批量操作
async batchPut(entries: Map<string, preferences.ValueType>): Promise<void> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
for (const [key, value] of entries) {
await this.prefStore.put(key, value)
}
// 统一刷新,减少IO操作
await this.prefStore.flush()
console.info(`批量写入完成: ${entries.size} 条`)
}
// 监听数据变更
onDataChange(key: string,
callback: (key: string, value: preferences.ValueType) => void): void {
if (!this.prefStore) return
this.prefStore.on('change', (keys: Array<string>) => {
if (keys.includes(key)) {
this.prefStore!.get(key, '').then((value) => {
callback(key, value)
})
}
})
}
// 删除指定键
async delete(key: string): Promise<void> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
await this.prefStore.delete(key)
await this.prefStore.flush()
}
// 清除所有数据
async clear(): Promise<void> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
await this.prefStore.clear()
await this.prefStore.flush()
}
// 检查键是否存在
async has(key: string): Promise<boolean> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
return await this.prefStore.has(key)
}
// 获取所有键
async getAllKeys(): Promise<Array<string>> {
if (!this.prefStore) {
throw new Error('Preferences未初始化')
}
return await this.prefStore.getAllKeys()
}
// 删除Preferences文件
async deletePreferences(): Promise<void> {
await preferences.deletePreferences(this.context, this.prefName)
this.prefStore = null
}
}8.6.2 6.5.2 数据迁移策略
// Preferences数据迁移管理器
class PreferencesMigrationManager {
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 版本化数据迁移
async migrateIfNeeded(): Promise<void> {
const currentVersion = 3 // 当前数据版本
const storedVersion = await this.getStoredVersion()
if (storedVersion >= currentVersion) {
console.info('数据已是最新版本,无需迁移')
return
}
console.info(`开始数据迁移: v${storedVersion} → v${currentVersion}`)
// 逐步执行迁移
let version = storedVersion
if (version < 1) {
await this.migrateToV1()
version = 1
}
if (version < 2) {
await this.migrateToV2()
version = 2
}
if (version < 3) {
await this.migrateToV3()
version = 3
}
// 更新版本号
await this.setStoredVersion(currentVersion)
console.info(`数据迁移完成: v${currentVersion}`)
}
// V1迁移:初始化默认设置
private async migrateToV1(): Promise<void> {
console.info('执行V1迁移: 初始化默认设置')
const pref = await preferences.getPreferences(this.context, 'app_preferences')
await pref.put('theme', 'light')
await pref.put('language', 'zh-CN')
await pref.put('fontSize', 16)
await pref.put('notifications_enabled', true)
await pref.flush()
}
// V2迁移:重构设置项
private async migrateToV2(): Promise<void> {
console.info('执行V2迁移: 重构设置项')
const pref = await preferences.getPreferences(this.context, 'app_preferences')
// 将旧的 'fontSize' 迁移到新的 'display.fontSize'
const fontSize = await pref.get('fontSize', 16) as number
await pref.put('display_font_size', fontSize)
await pref.delete('fontSize')
// 添加新设置
await pref.put('display_dark_mode', false)
await pref.flush()
}
// V3迁移:添加用户偏好
private async migrateToV3(): Promise<void> {
console.info('执行V3迁移: 添加用户偏好')
const pref = await preferences.getPreferences(this.context, 'app_preferences')
await pref.put('user_experience_level', 'intermediate')
await pref.put('auto_sync_enabled', true)
await pref.put('sync_interval_minutes', 30)
await pref.flush()
}
private async getStoredVersion(): Promise<number> {
const pref = await preferences.getPreferences(this.context, 'app_preferences')
return await pref.get('data_version', 0) as number
}
private async setStoredVersion(version: number): Promise<void> {
const pref = await preferences.getPreferences(this.context, 'app_preferences')
await pref.put('data_version', version)
await pref.flush()
}
}8.6.3 6.5.3 配置管理最佳实践
// 应用配置管理器 - 最佳实践
class AppConfigManager {
private prefManager: AdvancedPreferencesManager
private static instance: AppConfigManager | null = null
// 单例模式
static getInstance(context: common.UIAbilityContext): AppConfigManager {
if (!AppConfigManager.instance) {
const prefManager = new AdvancedPreferencesManager(context)
AppConfigManager.instance = new AppConfigManager(prefManager)
}
return AppConfigManager.instance
}
constructor(prefManager: AdvancedPreferencesManager) {
this.prefManager = prefManager
}
async initialize(): Promise<void> {
await this.prefManager.initialize()
}
// 主题配置
async getTheme(): Promise<string> {
return await this.prefManager.getString('theme', 'light')
}
async setTheme(theme: string): Promise<void> {
await this.prefManager.putString('theme', theme)
}
// 用户偏好配置
async getUserPreferences(): Promise<UserPreferences> {
return {
theme: await this.prefManager.getString('theme', 'light'),
language: await this.prefManager.getString('language', 'zh-CN'),
fontSize: await this.prefManager.getNumber('display_font_size', 16),
darkMode: await this.prefManager.getBoolean('display_dark_mode', false),
notificationsEnabled: await this.prefManager.getBoolean(
'notifications_enabled', true),
autoSyncEnabled: await this.prefManager.getBoolean(
'auto_sync_enabled', true),
syncIntervalMinutes: await this.prefManager.getNumber(
'sync_interval_minutes', 30)
}
}
async saveUserPreferences(prefs: UserPreferences): Promise<void> {
const entries = new Map<string, preferences.ValueType>()
entries.set('theme', prefs.theme)
entries.set('language', prefs.language)
entries.set('display_font_size', prefs.fontSize)
entries.set('display_dark_mode', prefs.darkMode)
entries.set('notifications_enabled', prefs.notificationsEnabled)
entries.set('auto_sync_enabled', prefs.autoSyncEnabled)
entries.set('sync_interval_minutes', prefs.syncIntervalMinutes)
await this.prefManager.batchPut(entries)
}
}
interface UserPreferences {
theme: string
language: string
fontSize: number
darkMode: boolean
notificationsEnabled: boolean
autoSyncEnabled: boolean
syncIntervalMinutes: number
}8.6.4 6.5.4 数据版本控制
// 数据版本控制器
class DataVersionController {
private static readonly VERSION_KEY = 'schema_version'
private static readonly MIGRATION_LOG_KEY = 'migration_log'
private prefStore: preferences.Preferences | null = null
async initialize(context: common.UIAbilityContext): Promise<void> {
this.prefStore = await preferences.getPreferences(context, 'version_control')
}
// 获取当前数据版本
async getCurrentVersion(): Promise<number> {
if (!this.prefStore) return 0
return await this.prefStore.get(DataVersionController.VERSION_KEY, 0) as number
}
// 设置数据版本
async setVersion(version: number): Promise<void> {
if (!this.prefStore) return
await this.prefStore.put(DataVersionController.VERSION_KEY, version)
await this.prefStore.flush()
}
// 记录迁移日志
async logMigration(fromVersion: number,
toVersion: number, description: string): Promise<void> {
if (!this.prefStore) return
const logEntry = {
from: fromVersion,
to: toVersion,
description: description,
timestamp: Date.now()
}
const existingLogs = await this.getMigrationLogs()
existingLogs.push(logEntry)
await this.prefStore.put(
DataVersionController.MIGRATION_LOG_KEY,
JSON.stringify(existingLogs)
)
await this.prefStore.flush()
}
// 获取迁移日志
async getMigrationLogs(): Promise<MigrationLogEntry[]> {
if (!this.prefStore) return []
const logStr = await this.prefStore.get(
DataVersionController.MIGRATION_LOG_KEY, '[]') as string
return JSON.parse(logStr) as MigrationLogEntry[]
}
}
interface MigrationLogEntry {
from: number
to: number
description: string
timestamp: number
}8.7 6.6 数据共享与交换
8.7.1 6.6.1 跨应用数据共享
import { dataShare } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// 跨应用数据共享客户端
class DataShareClient {
private context: common.UIAbilityContext
private dataShareProxy: dataShare.DataShareProxy | null = null
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 连接到DataShare服务
async connect(providerUri: string): Promise<void> {
try {
this.dataShareProxy = this.context.dataShareAcquire(providerUri)
console.info(`已连接到DataShare: ${providerUri}`)
} catch (error) {
console.error(`连接DataShare失败: ${JSON.stringify(error)}`)
throw error
}
}
// 查询数据
async query(uri: string, predicates: dataShare.DataSharePredicates,
columns: string[]): Promise<dataShare.ResultSet> {
if (!this.dataShareProxy) {
throw new Error('DataShare未连接')
}
const result = await this.dataShareProxy.query(uri, predicates, columns)
return result
}
// 插入数据
async insert(uri: string,
bucket: dataShare.DataShareValueObject): Promise<number> {
if (!this.dataShareProxy) {
throw new Error('DataShare未连接')
}
return await this.dataShareProxy.insert(uri, bucket)
}
// 更新数据
async update(uri: string,
predicates: dataShare.DataSharePredicates,
bucket: dataShare.DataShareValueObject): Promise<number> {
if (!this.dataShareProxy) {
throw new Error('DataShare未连接')
}
return await this.dataShareProxy.update(uri, predicates, bucket)
}
// 删除数据
async delete(uri: string,
predicates: dataShare.DataSharePredicates): Promise<number> {
if (!this.dataShareProxy) {
throw new Error('DataShare未连接')
}
return await this.dataShareProxy.delete(uri, predicates)
}
// 断开连接
disconnect(): void {
if (this.dataShareProxy) {
this.context.dataShareRelease(this.dataShareProxy)
this.dataShareProxy = null
}
}
}8.7.3 6.6.3 数据权限控制
// 数据权限管理器
class DataPermissionManager {
// 权限级别定义
static readonly PERMISSION_PUBLIC_READ = 'public_read'
static readonly PERMISSION_PUBLIC_WRITE = 'public_write'
static readonly PERMISSION_SAME_SIGNATURE = 'same_signature'
static readonly PERMISSION_SYSTEM = 'system_only'
// 检查访问权限
static checkPermission(requestingApp: string,
dataOwner: string, permission: string): boolean {
switch (permission) {
case DataPermissionManager.PERMISSION_PUBLIC_READ:
return true // 所有应用可读
case DataPermissionManager.PERMISSION_PUBLIC_WRITE:
return true // 所有应用可写
case DataPermissionManager.PERMISSION_SAME_SIGNATURE:
// 需要相同签名
return DataPermissionManager.hasSameSignature(requestingApp, dataOwner)
case DataPermissionManager.PERMISSION_SYSTEM:
// 仅系统应用
return DataPermissionManager.isSystemApp(requestingApp)
default:
return false
}
}
// 检查是否同签名
private static hasSameSignature(app1: string, app2: string): boolean {
// 实际实现中比较应用签名
return app1 === app2
}
// 检查是否系统应用
private static isSystemApp(bundleName: string): boolean {
// 实际实现中检查应用类型
return bundleName.startsWith('com.huawei.')
}
// 生成权限配置
static generatePermissionConfig(
rules: PermissionRule[]): DataPermissionConfig {
return {
version: 1,
rules: rules,
updatedAt: Date.now()
}
}
}
interface PermissionRule {
pathPattern: string
permission: string
allowedApps?: string[]
deniedApps?: string[]
}
interface DataPermissionConfig {
version: number
rules: PermissionRule[]
updatedAt: number
}8.7.4 6.6.4 数据格式标准化
// 标准化数据格式转换器
class DataFormatStandardizer {
// 将应用内部数据转换为标准交换格式
static toStandardFormat(data: Object,
schema: DataSchema): StandardDataFormat {
const result: StandardDataFormat = {
version: '1.0',
type: schema.type,
timestamp: Date.now(),
source: schema.source,
payload: {}
}
// 按照Schema映射字段
for (const field of schema.fields) {
const value = (data as Record<string, Object>)[field.sourceName]
if (value !== undefined) {
result.payload[field.targetName] = {
value: DataFormatStandardizer.convertValue(value, field.type),
type: field.type,
unit: field.unit
}
}
}
return result
}
// 将标准格式转换为应用内部格式
static fromStandardFormat(standard: StandardDataFormat,
schema: DataSchema): Object {
const result: Record<string, Object> = {}
for (const field of schema.fields) {
const standardField = standard.payload[field.targetName]
if (standardField) {
result[field.sourceName] = DataFormatStandardizer
.convertValue(standardField.value, field.type)
}
}
return result
}
// 值类型转换
private static convertValue(value: Object,
targetType: string): Object {
switch (targetType) {
case 'string':
return String(value)
case 'number':
return Number(value)
case 'boolean':
return Boolean(value)
case 'timestamp':
return typeof value === 'number' ? value : Date.parse(String(value))
default:
return value
}
}
}
interface DataSchema {
type: string
source: string
fields: FieldMapping[]
}
interface FieldMapping {
sourceName: string
targetName: string
type: string
unit?: string
required: boolean
}
interface StandardDataFormat {
version: string
type: string
timestamp: number
source: string
payload: Record<string, StandardFieldValue>
}
interface StandardFieldValue {
value: Object
type: string
unit?: string
}8.8 本章小结
本章全面介绍了HarmonyOS分布式数据管理的核心技术:
分布式数据对象:掌握了数据对象模型的设计原则,理解了属性级别的变更通知机制,学会了配置不同的同步策略(实时/定时/手动),以及冲突检测与解决的多种方案。
分布式数据同步:理解了三种同步模式的特点与适用场景,掌握了数据一致性保证机制(乐观锁、版本号),学会了离线数据处理和网络状态感知的实现方法。
关系型数据库高级:深入使用了RDB进行复杂查询,掌握了SQL查询优化技巧(谓词下推、索引优化、覆盖索引),学会了事务操作、批量插入和数据备份恢复。
键值对分布式存储:理解了分布式KV存储的架构,掌握了数据分区策略和加密存储的实现方法。
首选项与持久化:掌握了Preferences的异步操作、数据版本化迁移策略和配置管理最佳实践。
数据共享与交换:学会了跨应用数据共享的实现,掌握了DataShare服务的开发和数据权限控制方法。
8.9 练习题
8.9.1 一、单选题
1. 在HarmonyOS分布式KV存储中,以下哪个安全级别最高?
A. SecurityLevel.S1 B. SecurityLevel.S2 C. SecurityLevel.S3 D. SecurityLevel.S4
答案:D
解析: SecurityLevel 枚举值从 S1 到 S4,安全级别依次递增。S4 是最高安全级别,适用于存储高度敏感的数据,如用户密码、支付信息等。S1 适用于一般数据存储。
2. 在关系型数据库(RDB)中,以下哪种做法会导致索引失效?
A. 使用等值查询 WHERE id = 1 B. 对查询字段使用函数 WHERE UPPER(name) = 'ABC' C. 使用范围查询 WHERE age > 18 D. 使用复合索引的前缀列进行查询
答案:B
解析: 对查询字段使用函数(如 UPPER()、LOWER() 等)会导致数据库无法使用该列上的索引,因为索引存储的是原始值而非函数处理后的值。应改为在应用层处理或使用函数索引。
3. Preferences数据迁移的最佳实践是什么?
A. 直接覆盖旧数据 B. 使用版本号逐步迁移 C. 删除旧数据后重新创建 D. 不做迁移,让用户重新配置
答案:B
解析: 最佳实践是使用版本号进行逐步迁移。每次应用更新时检查存储的数据版本号,如果低于当前版本,则按版本顺序逐步执行迁移操作。这样可以确保数据完整性和向后兼容性。
4. 分布式数据对象的冲突解决策略中,哪种策略适用于多人协同编辑场景?
A. 本地优先(LOCAL_WINS) B. 远程优先(REMOTE_WINS) C. 时间戳优先(TIMESTAMP) D. 合并策略(MERGE)
答案:D
解析: 在多人协同编辑场景中,多个用户可能同时修改同一文档的不同部分。合并策略(MERGE)可以尝试将各方的修改进行合并,保留所有人的编辑内容,而不是简单地覆盖某一方的修改。
8.9.2 二、多选题
5. 以下哪些是HarmonyOS提供的分布式数据存储方式?
A. 分布式数据对象(Distributed Data Object) B. 分布式KV存储(Distributed KV Store) C. 关系型数据库(RDB) D. 首选项(Preferences) E. 文件系统(File)
答案:A、B
解析: HarmonyOS提供的分布式数据存储方式主要包括分布式数据对象和分布式KV存储,它们支持跨设备数据自动同步。关系型数据库(RDB)和首选项(Preferences)是本地存储方式,不直接支持分布式同步。文件系统也是本地存储。
6. 关于RDB(关系型数据库)的查询优化,以下哪些做法是正确的?
A. 使用 RdbPredicates 代替手写SQL B. 避免在WHERE子句中对索引列使用函数 C. 使用 SELECT * 获取所有列 D. 创建复合索引时遵循最左前缀原则 E. 使用预编译语句提高重复查询效率
答案:A、B、D、E
解析: 使用 RdbPredicates 更安全且高效(A正确);对索引列使用函数会导致索引失效(B正确);SELECT * 会返回不需要的列,浪费资源,应只查询需要的列(C错误);复合索引遵循最左前缀原则(D正确);预编译语句可以避免重复解析SQL(E正确)。
8.9.3 三、判断题
7. 分布式KV存储的 autoSync 选项设置为 true 时,数据变更会自动同步到所有已连接的设备。
答案:正确
解析: 当 autoSync 设置为 true 时,KV存储会在数据变更时自动触发同步操作,将变更推送到同一网络中的其他设备。开发者也可以通过设置为 false 来手动控制同步时机。
8. DataShare服务只能提供数据读取功能,不支持数据写入。
答案:错误
解析: DataShare服务支持完整的CRUD操作,包括查询(query)、插入(insert)、更新(update)和删除(delete)。Provider端需要实现这些接口来提供数据共享服务。