HarmonyOS开发:卡片数据共享——DataShare与卡片联动

举报
Jack20 发表于 2026/06/27 22:35:15 2026/06/27
【摘要】 HarmonyOS开发:卡片数据共享——DataShare与卡片联动📌 核心要点:FormAbility和UIAbility的上下文是隔离的,多卡片之间也各自独立,DataShare是打通这些数据孤岛的核心桥梁。 背景与动机你有没有遇到过这种场景:天气App的UIAbility里刷新了天气数据,但桌面上那张天气卡片还是显示旧数据。为什么?因为UIAbility和FormAbility的上...

HarmonyOS开发:卡片数据共享——DataShare与卡片联动

📌 核心要点:FormAbility和UIAbility的上下文是隔离的,多卡片之间也各自独立,DataShare是打通这些数据孤岛的核心桥梁。

背景与动机

你有没有遇到过这种场景:

天气App的UIAbility里刷新了天气数据,但桌面上那张天气卡片还是显示旧数据。为什么?因为UIAbility和FormAbility的上下文是隔离的,UIAbility更新了数据,FormAbility根本不知道。

或者:用户添加了两张天气卡片——一张显示当前天气,一张显示三天预报。UIAbility刷新数据后,只更新了当前天气卡片,三天预报卡片还是旧的。为什么?因为两张卡片是独立的formId,你得一个一个更新。

再或者:你的待办App有"今日待办"和"本周待办"两张卡片,用户在App里完成了一个待办,两张卡片应该同时更新。但你怎么确保两张卡片的数据一致?

这些问题的根源都是——数据孤岛。FormAbility、UIAbility、多张卡片之间,默认是互不相通的。要打通它们,你需要一个共享数据层。

DataShare就是鸿蒙提供的这个共享数据层。它本质上是一个跨进程的数据访问接口,UIAbility写数据,FormAbility读数据,多张卡片共享同一份数据源。

核心原理

数据共享架构

flowchart TB
    subgraph 数据写入方
        A[UIAbility] -->|写入| D[DataShare<br/>共享数据层]
        B[网络推送服务] -->|写入| D
    end
    
    subgraph 共享数据层
        D --> E[关系型数据库<br/>RDB]
        D --> F[键值对缓存]
    end
    
    subgraph 数据读取方
        D -->|读取| G[FormAbility<br/>卡片A]
        D -->|读取| H[FormAbility<br/>卡片B]
        D -->|读取| I[FormAbility<br/>卡片C]
    end
    
    subgraph 联动更新
        G --> J[formProvider.updateForm]
        H --> J
        I --> J
        J --> K[所有卡片同步刷新]
    end
    
    classDef writer fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
    classDef share fill:#FFF3E0,stroke:#E65100,color:#BF360C
    classDef reader fill:#E3F2FD,stroke:#1565C0,color:#0D47A1
    classDef update fill:#FCE4EC,stroke:#C62828,color:#B71C1C
    
    class A,B writer
    class D,E,F share
    class G,H,I reader
    class J,K update

关键点:DataShare是"中间人"——UIAbility负责写数据,FormAbility负责读数据,两者不直接通信。这种解耦架构的好处是:即使UIAbility进程被杀了,DataShare的数据还在,FormAbility照样能读到。

DataShare在卡片中的工作流程

sequenceDiagram
    participant UI as UIAbility
    participant DS as DataShare
    participant FA as FormAbility
    participant FP as formProvider
    participant Card as 卡片UI
    
    Note over UI,Card: 数据写入
    UI->>DS: 写入最新天气数据
    
    Note over UI,Card: 定时更新
    FP->>FA: onUpdateForm(formId)
    FA->>DS: 读取天气数据
    DS-->>FA: 返回数据
    FA->>FP: FormBindingData
    FP->>Card: 刷新UI
    
    Note over UI,Card: 主动更新
    UI->>DS: 写入新数据
    UI->>FP: updateForm(formId, data)
    FP->>Card: 刷新UI

数据共享方案对比

方案 适用场景 优点 缺点
DataShare 跨进程数据共享 标准接口、权限控制 开发复杂度高
分布式KV存储 轻量级键值共享 简单易用 不支持复杂查询
AppStorage 同进程内共享 零延迟 FormAbility和UIAbility不在同进程
Preferences 本地持久化 简单可靠 不支持跨进程

AppStorage为什么不行?因为FormAbility和UIAbility虽然可能在同一个应用里,但它们运行在不同的上下文中。AppStorage是进程内的全局状态,FormAbility的上下文和UIAbility的上下文是隔离的。

代码实战

基础用法:DataShare实现UIAbility与FormAbility数据共享

第一步:定义DataShare

// DataShareAbility.ets
import { DataShareExtensionAbility, relationalStore } from '@kit.ArkData'
import { hilog } from '@kit.PerformanceAnalysisKit'

// 数据库配置
const STORE_CONFIG: relationalStore.StoreConfig = {
  name: 'card_data.db',
  securityLevel: relationalStore.SecurityLevel.S1
}

// 表结构
const CREATE_TABLE_SQL = `
  CREATE TABLE IF NOT EXISTS weather_data (
    city TEXT PRIMARY KEY,
    temperature TEXT,
    weather TEXT,
    humidity TEXT,
    aqi TEXT,
    wind TEXT,
    update_time INTEGER
  )
`

export default class CardDataShare extends DataShareExtensionAbility {
  private store: relationalStore.RdbStore | null = null
  
  async onCreate(): void {
    hilog.info(0x0000, 'DataShare', 'onCreate')
    
    // 创建数据库
    this.store = await relationalStore.getRdbStore(this.context, STORE_CONFIG)
    
    // 创建表
    await this.store.executeSql(CREATE_TABLE_SQL)
    
    hilog.info(0x0000, 'DataShare', '数据库初始化完成')
  }
  
  // 查询数据
  async query(uri: string, columns: string[], predicates: dataSharePredicates.DataSharePredicates): Promise<relationalStore.ResultSet> {
    hilog.info(0x0000, 'DataShare', `query: ${uri}`)
    
    if (!this.store) {
      throw new Error('数据库未初始化')
    }
    
    // 解析URI获取查询条件
    const city = this.parseCityFromUri(uri)
    
    const predicates = new relationalStore.RdbPredicates('weather_data')
    if (city) {
      predicates.equalTo('city', city)
    }
    
    return this.store.querySql('SELECT * FROM weather_data WHERE city = ?', [city])
  }
  
  // 插入数据
  async insert(uri: string, valueBucket: relationalStore.ValuesBucket): Promise<number> {
    hilog.info(0x0000, 'DataShare', `insert: ${uri}`)
    
    if (!this.store) {
      throw new Error('数据库未初始化')
    }
    
    return this.store.insert('weather_data', valueBucket)
  }
  
  // 更新数据
  async update(uri: string, valueBucket: relationalStore.ValuesBucket, predicates: dataSharePredicates.DataSharePredicates): Promise<number> {
    hilog.info(0x0000, 'DataShare', `update: ${uri}`)
    
    if (!this.store) {
      throw new Error('数据库未初始化')
    }
    
    const city = this.parseCityFromUri(uri)
    const rdbPredicates = new relationalStore.RdbPredicates('weather_data')
    rdbPredicates.equalTo('city', city)
    
    return this.store.update(valueBucket, rdbPredicates)
  }
  
  // 删除数据
  async delete(uri: string, predicates: dataSharePredicates.DataSharePredicates): Promise<number> {
    hilog.info(0x0000, 'DataShare', `delete: ${uri}`)
    
    if (!this.store) {
      throw new Error('数据库未初始化')
    }
    
    const city = this.parseCityFromUri(uri)
    const rdbPredicates = new relationalStore.RdbPredicates('weather_data')
    rdbPredicates.equalTo('city', city)
    
    return this.store.delete(rdbPredicates)
  }
  
  private parseCityFromUri(uri: string): string {
    // URI格式: datashare:///com.example.weather/card_data/weather/北京
    const parts = uri.split('/')
    return parts[parts.length - 1] || '北京'
  }
}

第二步:在module.json5中声明DataShare

{
  "extensionAbilities": [
    {
      "name": "CardDataShare",
      "srcEntry": "./ets/datashare/CardDataShare.ets",
      "type": "dataShare",
      "uri": "datashare:///com.example.weather/card_data",
      "grantPermission": {
        "readPermission": "ohos.permission.READ_CARD_DATA",
        "writePermission": "ohos.permission.WRITE_CARD_DATA"
      }
    }
  ]
}

第三步:UIAbility写入数据

// WeatherUIAbility.ets
import { UIAbility } from '@kit.AbilityKit'
import { dataShare, relationalStore } from '@kit.ArkData'
import { hilog } from '@kit.PerformanceAnalysisKit'

const DATA_SHARE_URI = 'datashare:///com.example.weather/card_data'

export default class WeatherAbility extends UIAbility {
  private dataShareHelper: dataShare.DataShareHelper | null = null
  
  async onCreate(): Promise<void> {
    // 创建DataShareHelper
    this.dataShareHelper = dataShare.createDataShareHelper(this.context, DATA_SHARE_URI)
  }
  
  // 写入天气数据
  async saveWeatherData(city: string, weather: WeatherInfo): Promise<void> {
    if (!this.dataShareHelper) return
    
    const uri = `${DATA_SHARE_URI}/weather/${city}`
    
    const valueBucket: relationalStore.ValuesBucket = {
      city: city,
      temperature: weather.temperature,
      weather: weather.weather,
      humidity: weather.humidity,
      aqi: weather.aqi,
      wind: weather.wind,
      update_time: Date.now()
    }
    
    try {
      // 先尝试更新,失败则插入
      const updated = await this.dataShareHelper.update(uri, valueBucket)
      if (updated === 0) {
        await this.dataShareHelper.insert(uri, valueBucket)
      }
      
      hilog.info(0x0000, 'WeatherUI', `天气数据已保存: ${city}`)
      
      // 保存后通知所有卡片更新
      this.notifyCardsToUpdate()
    } catch (err) {
      hilog.error(0x0000, 'WeatherUI', `保存失败: ${err}`)
    }
  }
  
  // 通知卡片更新
  private notifyCardsToUpdate(): void {
    // 通过formProvider更新所有活跃卡片
    // 具体实现见后面的"卡片联动更新"
  }
}

第四步:FormAbility读取数据

// WeatherFormAbility.ets
import { formBindingData, FormExtensionAbility } from '@kit.AbilityKit'
import { dataShare, relationalStore } from '@kit.ArkData'
import { hilog } from '@kit.PerformanceAnalysisKit'

const DATA_SHARE_URI = 'datashare:///com.example.weather/card_data'

export default class WeatherFormAbility extends FormExtensionAbility {
  
  onAddForm(want: Want): formBindingData.FormBindingData {
    const city = want.parameters?.['city'] as string || '北京'
    
    // 从DataShare读取数据
    const weatherData = this.readWeatherFromDataShare(city)
    
    if (weatherData) {
      return formBindingData.createFormBindingData(weatherData)
    }
    
    // 读取失败,返回默认数据
    return formBindingData.createFormBindingData({
      city: city,
      temperature: '--°',
      weather: '加载中'
    })
  }
  
  onUpdateForm(formId: string): formBindingData.FormBindingData {
    // 更新时也从DataShare读取
    const city = this.getCityByFormId(formId)
    const weatherData = this.readWeatherFromDataShare(city)
    
    if (weatherData) {
      return formBindingData.createFormBindingData(weatherData)
    }
    
    return formBindingData.createFormBindingData({
      city: city || '--',
      temperature: '--°',
      weather: '更新失败'
    })
  }
  
  onRemoveForm(formId: string): void {
    hilog.info(0x0000, 'WeatherForm', `卡片删除: ${formId}`)
  }
  
  // 从DataShare读取天气数据
  private readWeatherFromDataShare(city: string): Record<string, string> | null {
    try {
      const helper = dataShare.createDataShareHelper(this.context, DATA_SHARE_URI)
      if (!helper) return null
      
      const uri = `${DATA_SHARE_URI}/weather/${city}`
      const columns = ['city', 'temperature', 'weather', 'humidity', 'aqi', 'wind', 'update_time']
      const predicates = new dataShare.DataSharePredicates()
      
      // 同步查询——注意:FormAbility回调中不能做异步操作
      // 实际项目中需要用缓存层包装DataShare读取
      const resultSet = helper.query(uri, columns, predicates)
      
      if (resultSet && resultSet.goToFirstRow()) {
        const data: Record<string, string> = {
          city: resultSet.getString(resultSet.getColumnIndex('city')),
          temperature: resultSet.getString(resultSet.getColumnIndex('temperature')),
          weather: resultSet.getString(resultSet.getColumnIndex('weather')),
          humidity: resultSet.getString(resultSet.getColumnIndex('humidity')),
          aqi: resultSet.getString(resultSet.getColumnIndex('aqi')),
          wind: resultSet.getString(resultSet.getColumnIndex('wind')),
          updateTime: this.formatTimestamp(resultSet.getLong(resultSet.getColumnIndex('update_time')))
        }
        resultSet.close()
        return data
      }
      
      if (resultSet) resultSet.close()
    } catch (err) {
      hilog.error(0x0000, 'WeatherForm', `DataShare读取失败: ${err}`)
    }
    
    return null
  }
  
  private formatTimestamp(ts: number): string {
    const date = new Date(ts)
    return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
  }
  
  private getCityByFormId(formId: string): string {
    return '北京'
  }
}

进阶用法:多卡片联动更新

当数据变化时,所有使用该数据的卡片都应该同步更新。这就是"联动更新"。

// CardUpdateCoordinator.ets —— 卡片更新协调器
import { formProvider, formBindingData } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'

// 卡片注册信息
interface CardRegistration {
  formId: string
  formName: string
  dataKeys: string[]  // 该卡片依赖的数据key
}

class CardUpdateCoordinator {
  private registrations: Map<string, CardRegistration> = new Map()
  private static instance: CardUpdateCoordinator
  
  // 数据key到卡片ID的映射
  private keyToCards: Map<string, Set<string>> = new Map()
  
  static getInstance(): CardUpdateCoordinator {
    if (!CardUpdateCoordinator.instance) {
      CardUpdateCoordinator.instance = new CardUpdateCoordinator()
    }
    return CardUpdateCoordinator.instance
  }
  
  // 注册卡片
  registerCard(formId: string, formName: string, dataKeys: string[]): void {
    this.registrations.set(formId, { formId, formName, dataKeys })
    
    // 建立数据key到卡片的映射
    dataKeys.forEach(key => {
      if (!this.keyToCards.has(key)) {
        this.keyToCards.set(key, new Set())
      }
      this.keyToCards.get(key)!.add(formId)
    })
    
    hilog.info(0x0000, 'Coordinator', `注册卡片: ${formId} (${formName}), 依赖: ${dataKeys.join(',')}`)
  }
  
  // 注销卡片
  unregisterCard(formId: string): void {
    const reg = this.registrations.get(formId)
    if (!reg) return
    
    // 清理映射
    reg.dataKeys.forEach(key => {
      this.keyToCards.get(key)?.delete(formId)
    })
    
    this.registrations.delete(formId)
    hilog.info(0x0000, 'Coordinator', `注销卡片: ${formId}`)
  }
  
  // 数据变化时,联动更新所有相关卡片
  async onDataChanged(changedKey: string, newData: Record<string, string>): Promise<void> {
    const affectedCards = this.keyToCards.get(changedKey)
    if (!affectedCards || affectedCards.size === 0) {
      hilog.info(0x0000, 'Coordinator', `无卡片依赖数据: ${changedKey}`)
      return
    }
    
    hilog.info(0x0000, 'Coordinator', `数据变化: ${changedKey}, 影响 ${affectedCards.size} 张卡片`)
    
    const bindingData = formBindingData.createFormBindingData(newData)
    const updatePromises: Promise<void>[] = []
    
    affectedCards.forEach(formId => {
      const promise = formProvider.updateForm(formId, bindingData)
        .then(() => {
          hilog.info(0x0000, 'Coordinator', `卡片更新成功: ${formId}`)
        })
        .catch((err: Error) => {
          hilog.error(0x0000, 'Coordinator', `卡片更新失败: ${formId}, ${err.message}`)
          // 卡片可能已删除
          if (err.message.includes('not exist')) {
            this.unregisterCard(formId)
          }
        })
      updatePromises.push(promise)
    })
    
    await Promise.all(updatePromises)
    hilog.info(0x0000, 'Coordinator', `联动更新完成: ${changedKey}`)
  }
  
  // 批量数据变化
  async onBatchDataChanged(changes: Map<string, Record<string, string>>): Promise<void> {
    // 收集所有受影响的卡片
    const affectedCards = new Set<string>()
    
    changes.forEach((_, key) => {
      const cards = this.keyToCards.get(key)
      if (cards) {
        cards.forEach(formId => affectedCards.add(formId))
      }
    })
    
    // 对每张受影响的卡片,合并所有相关数据后更新
    const updatePromises: Promise<void>[] = []
    
    affectedCards.forEach(formId => {
      const reg = this.registrations.get(formId)
      if (!reg) return
      
      // 合并该卡片依赖的所有变化数据
      let mergedData: Record<string, string> = {}
      reg.dataKeys.forEach(key => {
        const data = changes.get(key)
        if (data) {
          mergedData = { ...mergedData, ...data }
        }
      })
      
      if (Object.keys(mergedData).length === 0) return
      
      const bindingData = formBindingData.createFormBindingData(mergedData)
      const promise = formProvider.updateForm(formId, bindingData)
        .then(() => {
          hilog.info(0x0000, 'Coordinator', `卡片批量更新成功: ${formId}`)
        })
        .catch((err: Error) => {
          hilog.error(0x0000, 'Coordinator', `卡片批量更新失败: ${formId}`)
        })
      updatePromises.push(promise)
    })
    
    await Promise.all(updatePromises)
  }
}

const coordinator = CardUpdateCoordinator.getInstance()
export default coordinator

使用方式:

// 在FormAbility中注册
import coordinator from './CardUpdateCoordinator'

onAddForm(want: Want): formBindingData.FormBindingData {
  const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string
  const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string
  
  // 注册卡片,声明数据依赖
  const dataKeys = this.getDataKeysForCard(formName)
  coordinator.registerCard(formId, formName, dataKeys)
  
  return formBindingData.createFormBindingData(this.getInitialData(formName))
}

onRemoveForm(formId: string): void {
  coordinator.unregisterCard(formId)
}

// 在UIAbility中触发联动更新
async function onWeatherUpdated(city: string, data: Record<string, string>): Promise<void> {
  // 1. 保存到DataShare
  await saveToDataShare(city, data)
  
  // 2. 联动更新所有相关卡片
  await coordinator.onDataChanged(`weather_${city}`, data)
}

完整示例:待办卡片的数据一致性保障

待办场景对数据一致性要求很高——用户在App里完成一个待办,"今日待办"和"本周待办"两张卡片必须同时更新,不能出现"今日待办显示已完成,本周待办还显示未完成"的情况。

// TodoDataManager.ets —— 待办数据管理器
import { dataShare, relationalStore } from '@kit.ArkData'
import { formProvider, formBindingData } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'

const DATA_SHARE_URI = 'datashare:///com.example.todo/todo_data'

class TodoDataManager {
  private static instance: TodoDataManager
  private activeCards: Map<string, { formId: string; formName: string }> = new Map()
  private lastUpdateTime: Map<string, number> = new Map()
  private readonly MIN_INTERVAL = 5 * 60 * 1000  // 5分钟
  
  static getInstance(): TodoDataManager {
    if (!TodoDataManager.instance) {
      TodoDataManager.instance = new TodoDataManager()
    }
    return TodoDataManager.instance
  }
  
  // 注册卡片
  registerCard(formId: string, formName: string): void {
    this.activeCards.set(formId, { formId, formName })
  }
  
  // 注销卡片
  unregisterCard(formId: string): void {
    this.activeCards.delete(formId)
    this.lastUpdateTime.delete(formId)
  }
  
  // 待办完成——更新所有相关卡片
  async onTodoCompleted(todoId: string): Promise<void> {
    // 1. 更新数据库
    await this.markTodoCompletedInDB(todoId)
    
    // 2. 获取最新的待办统计数据
    const todayStats = await this.getTodayStats()
    const weekStats = await this.getWeekStats()
    
    // 3. 同时更新所有卡片(确保一致性)
    const updateTasks: Promise<void>[] = []
    
    this.activeCards.forEach((info, formId) => {
      // 检查更新间隔
      const lastUpdate = this.lastUpdateTime.get(formId) || 0
      if (Date.now() - lastUpdate < this.MIN_INTERVAL) {
        hilog.warn(0x0000, 'TodoData', `更新间隔不足: ${formId}`)
        return
      }
      
      let data: Record<string, string>
      
      switch (info.formName) {
        case 'todayTodoCard':
          data = todayStats
          break
        case 'weekTodoCard':
          data = weekStats
          break
        default:
          data = todayStats
      }
      
      const bindingData = formBindingData.createFormBindingData(data)
      const task = formProvider.updateForm(formId, bindingData)
        .then(() => {
          this.lastUpdateTime.set(formId, Date.now())
          hilog.info(0x0000, 'TodoData', `卡片更新成功: ${formId}`)
        })
        .catch((err: Error) => {
          hilog.error(0x0000, 'TodoData', `更新失败: ${err.message}`)
          if (err.message.includes('not exist')) {
            this.activeCards.delete(formId)
          }
        })
      
      updateTasks.push(task)
    })
    
    // 等待所有更新完成
    await Promise.all(updateTasks)
    hilog.info(0x0000, 'TodoData', '所有卡片更新完成')
  }
  
  private async markTodoCompletedInDB(todoId: string): Promise<void> {
    // 更新数据库
    hilog.info(0x0000, 'TodoData', `标记完成: ${todoId}`)
  }
  
  private async getTodayStats(): Promise<Record<string, string>> {
    return {
      totalCount: '5',
      completedCount: '2',
      latestTodo: '完成周报',
      date: new Date().toLocaleDateString()
    }
  }
  
  private async getWeekStats(): Promise<Record<string, string>> {
    return {
      totalCount: '25',
      completedCount: '10',
      completionRate: '40%',
      weekRange: '6/23 - 6/29'
    }
  }
}

const todoManager = TodoDataManager.getInstance()
export default todoManager

踩坑与注意事项

坑1:DataShare查询是同步还是异步?

DataShare的查询操作是异步的,但FormAbility的onUpdateForm回调是同步的。你不能在onUpdateForm里用await等DataShare返回。

解决方案:在FormAbility里维护一个内存缓存层。后台定时从DataShare读取数据到缓存,onUpdateForm直接读缓存。

// 缓存层
let dataCache: Map<string, Record<string, string>> = new Map()

// 后台定时刷新缓存(在FormAbility的onCreate中启动)
setInterval(async () => {
  const freshData = await readFromDataShare()
  dataCache = freshData
}, 30 * 60 * 1000)  // 30分钟刷新一次

// onUpdateForm直接读缓存
onUpdateForm(formId: string): formBindingData.FormBindingData {
  const cached = dataCache.get(formId)
  if (cached) {
    return formBindingData.createFormBindingData(cached)
  }
  return formBindingData.createFormBindingData({ title: '加载中' })
}

坑2:DataShare权限配置

DataShare需要配置读写权限。如果权限没配好,FormAbility读取数据时会抛异常。

// module.json5
{
  "extensionAbilities": [{
    "name": "CardDataShare",
    "type": "dataShare",
    "uri": "datashare:///com.example.weather/card_data",
    "grantPermission": {
      "readPermission": "ohos.permission.READ_CARD_DATA",
      "writePermission": "ohos.permission.WRITE_CARD_DATA"
    }
  }],
  "requestPermissions": [{
    "name": "ohos.permission.READ_CARD_DATA"
  }, {
    "name": "ohos.permission.WRITE_CARD_DATA"
  }]
}

坑3:联动更新的5分钟限制

联动更新时,每张卡片都受5分钟间隔限制。如果卡片A刚更新过,数据变化时卡片A不能立即更新,但卡片B可以。这会导致短暂的数据不一致。

解决方案:接受短暂不一致,下次更新时自动修正。或者用动态卡片,不受5分钟限制。

坑4:DataShare URI格式

DataShare的URI格式有严格要求,格式不对会查询失败。

// ✅ 正确格式
const uri = 'datashare:///com.example.weather/card_data/weather/北京'

// ❌ 错误格式
const uri = 'datashare://com.example.weather/card_data/weather/北京'  // 少了一个/
const uri = 'content://com.example.weather/card_data/weather/北京'   // Android格式

坑5:分布式KV存储的替代方案

如果你的数据很简单(就是几个键值对),用DataShare有点杀鸡用牛刀。分布式KV存储是更轻量的选择:

import { distributedKVStore } from '@kit.ArkData'

// 创建KV存储
const kvManager = distributedKVStore.createKVManager({
  bundleName: 'com.example.weather'
})

const kvStore = await kvManager.getKVStore({
  storeId: 'card_data',
  securityLevel: distributedKVStore.SecurityLevel.S1
})

// 写入
await kvStore.put('weather_beijing', JSON.stringify({ temp: '26°', weather: '晴' }))

// 读取
const value = await kvStore.get('weather_beijing')
const data = JSON.parse(value as string)

KV存储的优势是简单,缺点是不支持复杂查询。如果你的数据有结构化查询需求,还是得用DataShare。

HarmonyOS 6适配说明

  1. DataShare性能优化:6.0对DataShare的查询性能做了优化,增加了查询结果缓存。重复查询同一URI时,如果数据没变,直接返回缓存结果,不走数据库。

  2. 数据变化通知:6.0新增了DataShare的数据变化通知机制。你可以注册一个观察者,当DataShare中的数据变化时,自动收到通知:

// 6.0新增的数据变化观察
const helper = dataShare.createDataShareHelper(context, uri)

helper.on('dataChange', uri, () => {
  // 数据变化了,更新卡片
  this.updateCardFromDataShare()
})

这个特性极大简化了卡片联动更新的逻辑——不需要手动管理更新协调器了。

  1. 跨设备数据共享:6.0的DataShare支持跨设备数据共享。用户在手机上更新了待办,平板上的卡片自动同步。你只需要在DataShare配置中启用分布式模式:
{
  "extensionAbilities": [{
    "type": "dataShare",
    "uri": "datashare:///com.example.todo/todo_data",
    "distributed": true  // 6.0新增,启用跨设备共享
  }]
}
  1. KV存储事务支持:6.0的分布式KV存储新增了事务支持,可以保证多key的原子性更新:
// 6.0新增的事务
kvStore.beginTransaction()
try {
  await kvStore.put('today_count', '3')
  await kvStore.put('week_count', '15')
  kvStore.commit()
} catch (err) {
  kvStore.rollback()
}

总结

卡片数据共享是打通UIAbility、FormAbility、多卡片之间数据孤岛的关键。DataShare是标准方案,KV存储是轻量替代。联动更新需要协调器来管理卡片注册和数据分发。

核心要点回顾:

  • FormAbility和UIAbility上下文隔离,不能直接共享数据
  • DataShare是跨进程数据共享的标准方案
  • FormAbility回调是同步的,DataShare查询是异步的,需要缓存层
  • 多卡片联动更新需要协调器管理注册和分发
  • 5分钟更新限制会导致短暂的数据不一致
  • 简单数据用KV存储,复杂查询用DataShare
评估维度 说明
学习难度 ⭐⭐⭐⭐ DataShare配置、缓存层、联动更新都需要深入理解
使用频率 ⭐⭐⭐⭐ 大部分卡片都需要数据共享
重要程度 ⭐⭐⭐⭐ 没有数据共享,卡片就是信息孤岛

下一篇聊卡片性能优化——渲染效率、内存控制、更新频率平衡,让卡片又快又省电。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。