HarmonyOS 鸿蒙Next中KV数据库奇怪的bug

HarmonyOS 鸿蒙Next中KV数据库奇怪的bug

const KV_CONFIG: distributedKVStore.Options = {
  autoSync: true,
  securityLevel: distributedKVStore.SecurityLevel.S1
}
let old_kv_manager = distributedKVStore.createKVManager({
  context: old_context, bundleName: Constant.BUNDLE_NAME
})
let new_kv_manager = distributedKVStore.createKVManager({
  context: new_context, bundleName: Constant.BUNDLE_NAME
})
old_kv_manager.getAllKVStoreId(Constant.BUNDLE_NAME, (err, data) => {
  console.log('old all: ', JSON.stringify(data))
})
new_kv_manager.getAllKVStoreId(Constant.BUNDLE_NAME, (err, data) => {
  console.log('new all: ', JSON.stringify(data))
})
let old_kv_store = await old_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("Config", KV_CONFIG)
let new_kv_store = await new_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("Config", KV_CONFIG)
hilog.info(0x0000, 'RestoreOldContext', 'get old and new store')
await old_kv_store.put('A', 1)
old_kv_store.getResultSet(new distributedKVStore.Query(), (err, data) => {
  while (data.moveToNext()) {
    console.log('old: ', data.getEntry().key, JSON.stringify(data.getEntry().value))
  }
})
new_kv_store.getResultSet(new distributedKVStore.Query(), (err, data) => {
  while (data.moveToNext()) {
    console.log('new: ', data.getEntry().key, JSON.stringify(data.getEntry().value))
  }
})

上述代码中,old_context为UIAbilityContext,new_context为ApplicationContext,这两个context分别获取kvmanager,然后再区获取kvstore,实际上获取到的kvstore是同一个!!!日志证明:

01-17 13:43:35.421   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    new all:  ["Config","Config"]

01-17 13:43:35.421   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    old all:  ["Config","Config"]

01-17 13:43:35.443   52452-52452    A00000/appspaw...oreOldContext xxxx    I    get old and new store

01-17 13:43:35.460   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    old:  A {"type":5,"value":1}

01-17 13:43:35.460   52452-52452    A03D00/appspawn/JSAPP         com.onecourse.next    I    old:  privacy_viewed {"type":0,"value":"true"}

01-17 13:43:35.460   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    old:  restore_db {"type":4,"value":true}

01-17 13:43:35.460   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    old:  showing_course_id {"type":5,"value":17680248227663744}

01-17 13:43:35.461   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    new:  A {"type":5,"value":1}

01-17 13:43:35.461   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    new:  privacy_viewed {"type":0,"value":"true"}

01-17 13:43:35.461   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    new:  restore_db {"type":4,"value":true}

01-17 13:43:35.461   52452-52452    A03D00/appspawn/JSAPP         xxxx    I    new:  showing_course_id {"type":5,"value":17680248227663744}

这里的A成员之前是没有的,我只在oldstore进行了put动作,但是实际newstore也能查出来。所以这个context是没有用的吗?

实际上也不是的,当我删除所有old的代码,只通过ApplicationContext获取kvmanager然后获取kvstore,这时候获取到的kvstore是全新的,没有任何数据。

麻烦官方定位解释一下!


更多关于HarmonyOS 鸿蒙Next中KV数据库奇怪的bug的实战教程也可以访问 https://www.itying.com/category-93-b0.html

16 回复

【解决方案】

开发者您好,本地使用您提供的代码未复现出问题,测试结果是old_kv_store和new_kv_store是两个不同的对象。不运行old_kv_store相关代码后查询结果为空是因为没有添加数据,新增下面语句后可以打印日志。

await new_kv_store.put('A', 1)

如果未能解决问题,请提供以下信息:

1.当前开发工具版本(Help->About DevEco Studio)、运行时API版本(File->Project Structure->Project->Basic Info->Compatible SDK)、手机系统版本信息 (设置->关于手机)

完整复现代码:

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    const BUNDLE_NAME = 'BUNDLE_NAME';

    const KV_CONFIG: distributedKVStore.Options = {
      autoSync: true,
      securityLevel: distributedKVStore.SecurityLevel.S1
    }
    let old_kv_manager = distributedKVStore.createKVManager({
      context: this.context, bundleName: BUNDLE_NAME
    })
    let new_kv_manager = distributedKVStore.createKVManager({
      context: this.context.getApplicationContext(), bundleName: BUNDLE_NAME
    })
    old_kv_manager.getAllKVStoreId(BUNDLE_NAME, (err, data) => {
      console.log('old all: ', JSON.stringify(data))
    })
    new_kv_manager.getAllKVStoreId(BUNDLE_NAME, (err, data) => {
      console.log('new all: ', JSON.stringify(data))
    })
    let old_kv_store = await old_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("Config", KV_CONFIG)
    let new_kv_store = await new_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("Config", KV_CONFIG)
    hilog.info(0x0000, 'RestoreOldContext', 'get old and new store')
     
    await old_kv_store.put('A', 1)
    await new_kv_store.put('A', 1)
    old_kv_store.getResultSet(new distributedKVStore.Query(), (err, data) => {
      while (data.moveToNext()) {
        console.log('old: ', data.getEntry().key, JSON.stringify(data.getEntry().value))
      }
    })
    new_kv_store.getResultSet(new distributedKVStore.Query(), (err, data) => {
      while (data.moveToNext()) {
        console.log('new: ', data.getEntry().key, JSON.stringify(data.getEntry().value))
      }
    })
 
  }

更多关于HarmonyOS 鸿蒙Next中KV数据库奇怪的bug的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


let old_kv_manager = distributedKVStore.createKVManager({
  context: old_context, bundleName: Constant.BUNDLE_NAME
})
let new_kv_manager = distributedKVStore.createKVManager({
  context: new_context, bundleName: Constant.BUNDLE_NAME
})

这里创建的都是同一个数据库实例,只是不同引用,所以你操作old,new也会同步变化,而且你使用的options和storeId都是一样的,至于你所说的删除了所有old代码,因没看到你的具体删除代码,猜测是指删库行为,你用new再去获取相当于重新申请创建数据库并分配一个新的ID值。

解释如下:

ApplicationContext跟UIAbilityContext实际上都是继承于Context,而BaseContext是所有Context类型的父类,所以你用上面两个的Context获取数据库实例,最终用的还是同一个,至于数据库的唯一性取决于你配置的options与storeId。

cke_53155.png

cke_14815.png

cke_15254.png

cke_25420.png

相关文档:

如果是同一个实例,怎么可能getallstoreid会返回两个config,

我说的删除代码是指不执行old的那行getkvstore,

IDE版本是多少?运行设备的API版本是多少?我这边打印是只有一个config。

  • 你的代码中两个KVManager使用了相同的:
  • bundleName: Constant.BUNDLE_NAME
  • storeId: "Config"
  • securityLevel: S1 因此系统会返回同一个物理存储实例,无论使用UIAbilityContext还是ApplicationContext。KVStore 的「物理唯一标识」不是 Context,而是 bundleName + storeId + securityLevel

可以通过不同storeId隔离存储空间。

// 修改storeId实现隔离
let old_store = await old_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("OldConfig", KV_CONFIG);
let new_store = await new_kv_manager.getKVStore<distributedKVStore.DeviceKVStore>("NewConfig", KV_CONFIG);

不理解啊,如果是唯一标识,怎么能创建出来两个呢,

不是,你的问题不是创建了2个结果是1个吗?因为你创建虽然用过来不同的Context,但是bundleName + storeId + securityLevel,这3个都一样,所以创建的2个其实还是一个,现在用不同的storeId的方法创建不同的2个。

不是的,你看getallstoreid查出来的,真的有两个,

这个是不同的CONTEXT有不同的存储分区。

官网原文:应用创建的数据库与其上下文(Context)有关,即使使用同样的数据库名称,但不同的应用上下文,会产生多个数据库,例如每个UIAbility都有各自的上下文。

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-persistence-by-rdb-store#开发步骤

建议了解下context文档

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-sandbox-directory#应用文件目录与应用文件路径

数据库调试文档

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/vector-store-debug-tool

你仔细看一下我发的内容,我也以为是不同context不同数据库,但是数据获取到的是一个,

UIAbilityContext 和 ApplicationContext 在分布式 KV 里,不是“存储隔离维度”。

只要 bundleName + storeId + securityLevel 一样,

👉 不管你用哪个 Context,拿到的都是同一个物理 KVStore

Context 是否推荐作为 KV 主要入口 原因
UIAbilityContext ✅ 强烈推荐 能保证数据已恢复完成
ApplicationContext ⚠️ 仅限特定场景 可能读到“未就绪数据”

那如何解释我用kvmanager查询allstoreid返回了两个同名store的问题,

而且我如果删掉old的代码,直接使用application去获取kvstore,拿到的是一个新的数据库,里面什么都没有,

鸿蒙Next的KV数据库bug可能涉及数据读写异常、键值对丢失或并发访问冲突。具体表现包括数据持久化失败、查询结果不一致或应用崩溃。建议检查数据库初始化配置、数据操作逻辑及多线程同步机制。

根据您提供的代码和日志,这并非一个bug,而是HarmonyOS Next中分布式KV数据库的设计机制。

核心原因在于:对于同一个应用,通过不同Context创建的KVManager,如果指定了相同的bundleNamestoreId,它们访问的是同一个物理KV数据库实例。

在您的场景中:

  1. old_context (UIAbilityContext) 和 new_context (ApplicationContext) 都属于同一个应用
  2. 您使用相同的 Constant.BUNDLE_NAME"Config" 作为参数来创建KVManager和获取KVStore。
  3. 因此,old_kv_storenew_kv_store 实际上是指向同一个底层数据库“Config”的两个句柄。通过任何一个句柄写入数据(如old_kv_store.put('A', 1)),另一个句柄自然能读取到。

日志分析:

  • getAllKVStoreId 两次都输出了 ["Config","Config"],这进一步证实了对于同一个bundleName,两个KVManager管理的是同一组Store ID列表。
  • 查询日志显示,oldnew 查询出的数据完全一致,包括您刚插入的 'A':1,这符合上述设计。

关于您后续的测试现象: 当您删除所有old_context相关代码,仅使用ApplicationContext时,如果数据库“Config”之前从未被成功创建并持久化过,那么获取到的就是一个全新的空数据库。这并不能证明Context无效,反而说明了:

  1. 首次创建数据库时,需要成功完成put等写入操作,数据才会被持久化。
  2. 只要数据库被成功创建并持久化,之后无论通过该应用下的哪个Context(UIAbilityContext或ApplicationContext),只要bundleNamestoreId相同,访问的都是同一个持久化的数据库。

结论: distributedKVStore的设计是以应用(由bundleName标识)和数据库名storeId)为维度进行隔离的,而不是以Context为维度。在同一个应用内,Context主要用于获取访问权限和资源路径,在访问同一持久化数据库时,它们的作用是等效的。您观察到的行为是符合预期的。

回到顶部