往期知识点记录:

Utils子系统是OpenHarmony的公共基础库,存放OpenHarmony通用的基础组件。这些基础组件可被OpenHarmony各业务子系统及上层应用所使用。公共基础库在不同平台上提供的能力:

  • LiteOS-M内核:KV(key value)存储、文件操作、定时器、Dump系统属性。
  • LiteOS-A内核:KV(key value)存储、定时器、JS API(设备查询,数据存储)、Dump系统属性。

本文介绍下移植开发板时如何适配utils子系统之KV存储部件,并介绍下相关的运行机制原理。KV存储部件定义在utils\native\lite\。源代码目录如下:

utils/native/lite/              # 公共基础库根目录
├── file                        # 文件接口实现
├── hals                        # HAL目录
│   └── file                    # 文件操作硬件抽象层头文件
├── include                     # 公共基础库对外接口文件
├── js                          # JS API目录                 
│   └── builtin
│       ├── common
│       ├── deviceinfokit       # 设备信息Kit
│       ├── filekit             # 文件Kit
│       └── kvstorekit          # KV存储Kit
├── kal                         # KAL目录
│   └── timer                   # Timer的KAL实现
├── kv_store	                # KV存储实现
│   ├── innerkits               # KV存储内部接口
│   └── src	                    # KV存储源文件
├── memory
│   └── include                 # 内存池管理接口
├── os_dump                     # Dump系统属性
└── timer_task                  # Timer实现

1、KV存储部件适配示例

1.1 配置产品解决方案config.json

utils子系统之KV存储部件的适配示例可以参考vendor\ohemu\qemu_csky_mini_system_demo\config.json,代码片段如下。⑴处用于配置子系统的KV存储部件。⑵处指定在开发板目录中适配目录,这个适配目录下需要创建目录device\qemu\SmartL_E802\adapter\hals\utils\file\,之前的移植案例与原理文章中介绍过vendor_adapter_dir目录。对于KV存储部件,与适配syspara_lite部件类似,适配kv_store部件时,键值对会写到文件中。在轻量系统中,文件操作相关接口有POSIX接口与HalFiles接口这两套实现。部件文件utils\native\lite\kv_store\src\BUILD.gn中声明了enable_ohos_utils_native_lite_kv_store_use_posix_kv_api配置参数,默认值为true。当使用默认值或主动设置为true时,使用POSIX相关的接口,否则使用HalFiles相关的接口。如果使用HalFiles相关的接口,需要适配UtilsFile部件,参考之前的移植案例与原理文章即可。

      {
        "subsystem": "utils",
        "components": [
          { "component": "file", "features":[] },
⑴        { "component": "kv_store", "features":[] }
        ]
      }
    ],
⑵   "vendor_adapter_dir": "//device/qemu/SmartL_E802/adapter",

1.2 适配后运行示例代码

适配后,编写如下代码,就可以使用KV存储功能。下面是示例代码程序片段,实现保存键值,通过key键获取对应的值,删除键值等等。

// 存储/更新key对应数据项
const char key1[] = "key_sample";
const char defValue[] = "test case of key value store.";
int ret = UtilsSetValue(key1, defValue);

// 根据key获取对应数据项
char value1[32] = {0};
ret = UtilsGetValue(key1, value1, 32);

// 删除key对应数据项
UtilsDeleteValue(key1);

2、KV存储部件kvstore_common通用代码

2.1 结构体、函数声明、变量

在文件utils\native\lite\kv_store\src\kvstore_common\kvstore_common.h中声明了KV存储的函数,并定义了结构体KvItem。⑴处定义了键值的最大长度,⑵处的FEATURE_KV_CACHE宏开关,定义在utils\native\lite\include\utils_config.h,默认是定义了该宏的。⑶处定义的结构体,成员包含键值,以及前驱后继结构体指针。

⑴  #define MAX_KEY_LEN 32
    #define MAX_VALUE_LEN 128

    boolean IsValidChar(const char ch);
    boolean IsValidValue(const char* value, unsigned int len);
    boolean IsValidKey(const char* key);

⑵  #ifdef FEATURE_KV_CACHE
⑶  typedef struct KvItem_ {
        char* key;
        char* value;
        struct KvItem_* next;
        struct KvItem_* prev;
    } KvItem;

    void DeleteKVCache(const char* key);
    void AddKVCache(const char* key, const char* value, boolean isNew);
    int GetValueByCache(const char* key, char* value, unsigned int maxLen);
    int ClearKVCacheInner(void);
    #endif

在文件utils\native\lite\kv_store\src\kvstore_common\kvstore_common.c中定义了内部全局变量,g_itemHeader、g_itemTail分别指向键值链表的首尾,g_sum记录键值对数量。

#ifdef FEATURE_KV_CACHE
static KvItem* g_itemHeader = NULL;
static KvItem* g_itemTail = NULL;
static int g_sum = 0;
#endif

2.2 键值有效性判断函数

函数IsValidKey、IsValidValue分别用于判断键、值是否为有效的。⑴处表明键值必须为小写的字符,数值,下划线或者点符号。使用IsValidValue判断值是否有效时,需要传入2个参数,一个是要判断的字符串值的指针,一个是长度len。⑵处获取字符串的个数,包含最后的null;不超过最大长度MAX_VALUE_LEN。然后进一步判断,如果长度为0,长度大于等于最大长度MAX_VALUE_LEN(因为需要末尾的null,等于也不行),或者大于参数中传递的长度时,都会返回FALSE,否则返回TRUE。使用IsValidKey判断键是否有效时,先调用函数IsValidValue确保长度是有效的,然后调用函数IsValidChar判断每一个字符都是有效的,只能是小写字符,数值或者点符号。

    boolean IsValidChar(const char ch)
    {
⑴      if (islower(ch) || isdigit(ch) || (ch == '_') || (ch == '.')) {
            return TRUE;
        }
        return FALSE;
    }

    boolean IsValidValue(const char* value, unsigned int len)
    {
        if (value == NULL) {
            return FALSE;
        }
⑵      size_t valueLen = strnlen(value, MAX_VALUE_LEN);
        if ((valueLen == 0) || (valueLen >= MAX_VALUE_LEN) || (valueLen >= len)) {
            return FALSE;
        }
        return TRUE;
    }

    boolean IsValidKey(const char* key)
    {
        if (!IsValidValue(key, MAX_KEY_LEN)) {
            return FALSE;
        }
        size_t keyLen = strnlen(key, MAX_KEY_LEN);
        for (size_t i = 0; i < keyLen; i++) {
            if (!IsValidChar(key[i])) {
                return FALSE;
            }
        }
        return TRUE;
    }

2.3 根据键删除值DeleteKVCache

⑴处的函数FreeItem释放结构体成员变量指针,结构体占用的内存。函数DeleteKVCache用于删除键参数对应的值。⑵处从键值对头部的第一个键值开始,循环键值链表,比对参数中的键和循环到的键。如果不相等,则循环下一个链表节点。如果一直不相等,并且循环到的节点为NULL,说明链表中不存在相同的键,直接返回不需要执行删除操作。如果执行到⑶,说明键值对中存在匹配的键,键值对总数减去1。⑷处对删键值后的数量的各种情况进行判断,如果键值对数量为0,键值对首尾指针设置为NULL;如果删除的是队首元素,队尾元素,队中元素,分别处理。⑸处释放要删除的结构体占用的内存。

⑴   static void FreeItem(KvItem* item)
    {
        if (item == NULL) {
            return;
        }
        if (item->key != NULL) {
            free(item->key);
        }
        if (item->value != NULL) {
            free(item->value);
        }
        free(item);
    }

    void DeleteKVCache(const char* key)
    {
        if (key == NULL || g_itemHeader == NULL) {
            return;
        }

⑵      KvItem* item = g_itemHeader;
        while (strcmp(key, item->key) != 0) {
            item = item->next;
            if (item == NULL) {
                return;
            }
        }
⑶      g_sum--;
⑷      if (g_sum == 0) {
            g_itemHeader = NULL;
            g_itemTail = NULL;
        } else if (item == g_itemHeader) {
            g_itemHeader = item->next;
            g_itemHeader->prev = NULL;
        } else if (item == g_itemTail) {
            g_itemTail = item->prev;
            g_itemTail->next = NULL;
        } else {
            item->prev->next = item->next;
            item->next->prev = item->prev;
        }
⑸      FreeItem(item);
    }

2.4 添加缓存AddKVCache

函数AddKVCache添加一对键值到缓存里。共三个参数,前两者为键和值;第三个参数boolean isNew为true时,会先尝试删除旧的键值对,只保留最新的键值数据。如果为false,可能存在键值相同的两个键值对,但是值不同。做完必要的参数非空校验后,执行⑴获取键、值的字符长度。⑵处处理是否删除旧的键值对数据。⑶处为键值对结构体申请内存区域,内存区域置空。⑷处为键、值分别申请内存区域,申请的时候多加1个字符长度用于保存null空字符。⑸处把参数传入的键值数据复制到键值对结构体对应的内存区域。⑹处理缓存内没有键值数据的情况。当缓存有键值信息时,新加入的放入键值对链表头部。⑻处当缓存数量大于最大缓存数时,依次从尾部删除。

void AddKVCache(const char* key, const char* value, boolean isNew)
{
    if (key == NULL || value == NULL) {
        return;
    }

⑴  size_t keyLen = strnlen(key, MAX_KEY_LEN);
    size_t valueLen = strnlen(value, MAX_VALUE_LEN);
    if ((keyLen >= MAX_KEY_LEN) || (valueLen >= MAX_VALUE_LEN)) {
        return;
    }
⑵  if (isNew) {
        DeleteKVCache(key);
    }
⑶  KvItem* item = (KvItem *)malloc(sizeof(KvItem));
    if (item == NULL) {
        return;
    }
    (void)memset_s(item, sizeof(KvItem), 0, sizeof(KvItem));
⑷  item->key = (char *)malloc(keyLen + 1);
    item->value = (char *)malloc(valueLen + 1);
    if ((item->key == NULL) || (item->value == NULL)) {
        FreeItem(item);
        return;
    }
⑸  if ((strcpy_s(item->key, keyLen + 1, key) != EOK) ||
        (strcpy_s(item->value, valueLen + 1, value) != EOK)) {
        FreeItem(item);
        return;
    }
    item->prev = NULL;
    item->next = NULL;
⑹  if (g_itemHeader == NULL) {
        g_itemHeader = item;
        g_itemTail = item;
        g_sum++;
        return;
    }
⑺  item->next = g_itemHeader;
    g_itemHeader->prev = item;
    g_itemHeader = item;
    g_sum++;
⑻  while (g_sum > MAX_CACHE_SIZE) {
        KvItem* needDel = g_itemTail;
        g_itemTail = g_itemTail->prev;
        FreeItem(needDel);
        g_itemTail->next = NULL;
        g_sum--;
    }
}

2.5 从缓存中获取值GetValueByCache

函数GetValueByCache用于从缓存中读取值。共三个参数,前两者为键和值,const char* ke为键,输入参数;char* value为输出参数,用于保存返回的值;第三个参数unsigned int maxLen用于限制获取的值的最大长度。该函数的返回值代表获取成功EC_SUCCESS或失败EC_FAILURE。做完必要的参数非空校验后,执行⑴循环键值对链表,获取对应键的键值结构体。如果获取不到,则返回EC_FAILURE;否则,执行⑵获取值的长度,当这个长度超出值的最大长度时,返回EC_FAILURE。⑶处,如果获取的值的长度超出参数传入的长度,不会截断,而是返回错误。从item->value把值复制到输出参数里,如果失败也会返回错误。

int GetValueByCache(const char* key, char* value, unsigned int maxLen)
{
    if (key == NULL || value == NULL || g_itemHeader == NULL) {
        return EC_FAILURE;
    }

    KvItem* item = g_itemHeader;
⑴  while (strcmp(key, item->key) != 0) {
        item = item->next;
        if (item == NULL) {
            return EC_FAILURE;
        }
    }
⑵  size_t valueLen = strnlen(item->value, MAX_VALUE_LEN);
    if (valueLen >= MAX_VALUE_LEN) {
        return EC_FAILURE;
    }
⑶  if ((valueLen >= maxLen) || (strcpy_s(value, maxLen, item->value) != EOK)) {
        return EC_FAILURE;
    }
    return EC_SUCCESS;
}

2.6 清除缓存ClearKVCacheInner

清除缓存函数ClearKVCacheInner会把缓存的键值对全部清空,返回清除成功或失败的返回值。⑴如果键值对链表头节点为空,返回成功。⑵处循环键值对链表每一个键值对元素,一一删除。每删除一个,执行⑶,把基础缓存的键值对数目减1。

int ClearKVCacheInner(void)
{
⑴  if (g_itemHeader == NULL) {
        return EC_SUCCESS;
    }
    KvItem* item = g_itemHeader;
⑵  while (item != NULL) {
        KvItem* temp = item;
        item = item->next;
        FreeItem(temp);
⑶      g_sum--;
    }
    g_itemHeader = NULL;
    g_itemTail = NULL;

    return (g_sum != 0) ? EC_FAILURE : EC_SUCCESS;
}

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview

图片
在这里插入图片描述

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐