awtk/docs/how_to_use_app_conf.md
2020-08-16 09:10:01 +08:00

616 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 如何存取应用程序的配置信息
## 1. 介绍
AWTK 提供了 [app_conf](https://github.com/zlgopen/awtk/blob/master/docs/manual/app_conf_t.md) 接口,用于保存或读取应用程序的配置信息。其特点有:
* 线程安全,可以多线程访问。
* 观察者模式,配置变化时通知关注者。
* 统一接口,不用关注存储格式和存储介质。
* 接口支持多种数据类型,开发者无需在字符串和其它类型之间转换。
配置信息可以保存为任意格式,目前支持下列文件格式(其它格式开发者可以自行增加):
* [INI](https://zh.wikipedia.org/wiki/INI)
* [JSON](https://zh.wikipedia.org/wiki/JSON)
* [UBJSON](https://en.wikipedia.org/wiki/UBJSON)
配置信息可以存放在任何介质上,目前支持以下介质:
* 文件系统。
* Flash(TODO)
## 2. 初始化(任意选一种格式即可)
[app_conf](https://github.com/zlgopen/awtk/blob/master/docs/manual/app_conf_t.md) 作为可选组件,需要开发者自己初始化。
> 初始化函数一般在 application_init 函数中调用。所有读写等函数,必须在成功初始化之后才能调用。
配置文件通常保存在用户目录下比如应用程序的名称为demo存储格式为json那么在不同的平台下存放的位置为
* linux
```
/demo/app_conf.json
```
* macos
```
/demo/app_conf.json
```
* windows
```
/AppData/Local/demo/app_conf.json
```
* 嵌入式
```
/appdata/demo/app_conf.json
```
> 应用程序在启动时,会打印配置文件的位置,以实际位置为准。
* 2.1 使用 ini 格式时的初始方法
包含头文件:
```c
#include "conf_io/app_conf_init_ini.h"
```
调用初始化函数
```c
/**
* @method app_conf_init_ini
*
* 初始化应用程序的配置信息。
*
* @param {const char*} app_name 应用程序名称。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_init_ini(const char* app_name)
```
示例:
```c
ENSURE(app_conf_init_ini("demo") == RET_OK);
```
* 2.2 使用 json 格式时的初始方法
包含头文件:
```c
#include "conf_io/app_conf_init_json.h"
```
调用初始化函数
```c
/**
* @method app_conf_init_json
*
* 初始化应用程序的配置信息。
*
* @param {const char*} app_name 应用程序名称。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_init_json(const char* app_name)
```
示例:
```c
ENSURE(app_conf_init_json("demo") == RET_OK);
```
* 2.3 使用 ubjson 格式时的初始方法
包含头文件:
```c
#include "conf_io/app_conf_init_ubjson.h"
```
调用初始化函数
```c
/**
* @method app_conf_init_ubjson
*
* 初始化应用程序的配置信息。
*
* @param {const char*} app_name 应用程序名称。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_init_ubjson(const char* app_name)
```
示例:
```c
ENSURE(app_conf_init_ubjson("demo") == RET_OK);
```
## 3. 存取配置信息
### 3.1 包含头文件:
app\_conf 作为可选组件,需要开发者自己包含头文件。
```c
#include "conf_io/app_conf.h"
```
### 3.2 修改配置信息
```c
/**
* @method app_conf_set_int
* 设置整数类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {int32_t} v 配置项的值。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_set_int(const char* key, int32_t v);
/**
* @method app_conf_set_int64
* 设置 64 位整数类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {int64_t} v 配置项的值。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_set_int64(const char* key, int64_t v);
/**
* @method app_conf_set_bool
* 设置 bool 类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {bool_t} v 配置项的值。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_set_bool(const char* key, bool_t v);
/**
* @method app_conf_set_double
* 设置双精度类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {double} v 配置项的值。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_set_double(const char* key, double v);
/**
* @method app_conf_set_str
* 设置字符串类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {const char*} v 配置项的值。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_set_str(const char* key, const char* v);
```
示例:
```c
app_conf_set_bool("wifi.on", TRUE);
app_conf_set_str("wifi.name", "awtk");
app_conf_set_int("server.port", 8080);
app_conf_set_double("server.timeout", 1.0);
```
使用 ini 格式时,实际存为:
```ini
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
```
使用 json 格式时,实际存为:
```ini
{
"wifi" : {
"on" : true,
"name" : "awtk"
},
"server" : {
"port" : 8080,
"timeout" : 1
}
}
```
使用 ubjson 格式时,实际存为二进制格式,这里不再举例。
### 3.3 读取配置信息
```c
/**
* @method app_conf_get_int
* 获取整数类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {int32_t} defval 缺省值。
*
* @return {int32_t} 返回配置项的值(如果不存在返回缺省值)。
*/
int32_t app_conf_get_int(const char* key, int32_t defval);
/**
* @method app_conf_get_int64
* 获取 64 位整数类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {int64_t} defval 缺省值。
*
* @return {int64_t} 返回配置项的值(如果不存在返回缺省值)。
*/
int64_t app_conf_get_int64(const char* key, int64_t defval);
/**
* @method app_conf_get_bool
* 获取 bool 类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {bool_t} defval 缺省值。
*
* @return {bool_t} 返回配置项的值(如果不存在返回缺省值)。
*/
bool_t app_conf_get_bool(const char* key, bool_t defval);
/**
* @method app_conf_get_double
* 获取单精度浮点数类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {double} defval 缺省值。
*
* @return {double} 返回配置项的值(如果不存在返回缺省值)。
*/
double app_conf_get_double(const char* key, double defval);
/**
* @method app_conf_get_str
* 获取字符串类型配置项的值。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
* @param {const char*} defval 缺省值。
*
* @return {const char*} 返回配置项的值(如果不存在返回缺省值)。
*/
const char* app_conf_get_str(const char* key, const char* defval);
```
示例:
```c
on = app_conf_get_bool("wifi.on", FALSE);
port = app_conf_get_int("server.port", 0);
timeout = app_conf_get_double("server.timeout", 0);
name = app_conf_get_str("wifi.name", NULL);
```
3.4 删除配置信息
```c
/**
* @method app_conf_remove
* 删除配置项。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_remove(const char* key);
```
示例:
```c
app_conf_set_str("wifi.password", "123123");
assert(app_conf_exist("wifi.password"));
app_conf_remove("wifi.password");
assert(!app_conf_exist("wifi.password"));
```
3.5 检查配置信息是否存在
```c
/**
* @method app_conf_exist
* 检查配置项是否存在。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
*
* @return {bool_t} 返回 TRUE 表示存在FALSE 表示不存在。
*/
bool_t app_conf_exist(const char* key);
```
示例:
```c
app_conf_set_str("wifi.password", "123123");
assert(app_conf_exist("wifi.password"));
app_conf_remove("wifi.password");
assert(!app_conf_exist("wifi.password"));
```
3.6 持久保存配置信息
```c
/**
* @method app_conf_save
* 持久保存配置。
*
* @annotation ["static", "scriptable"]
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_save(void);
```
示例:
```c
app_conf_save();
```
3.7 关注配置信息的变化
```c
/**
* @method app_conf_on_changed
* 注册配置变化事件。
*
* @annotation ["static", "scriptable"]
* @param {event_func_t} on_event 事件处理函数。
* @param {void*} ctx 事件处理函数上下文。
* @return {uint32_t} 返回 id用于 app_conf_off_changed。
*/
uint32_t app_conf_on_changed(event_func_t on_event, void* ctx);
/**
* @method app_conf_off_changed
* 注销配置变化事件。
*
* @annotation ["static", "scriptable"]
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_off_changed(uint32_t id);
/**
* @method app_conf_off_changed_by_ctx
* 注销配置变化事件。
*
* @annotation ["static"]
* @param {void*} ctx 事件处理函数上下文。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_off_changed_by_ctx(void* ctx);
```
示例:
```c
static ret_t app_conf_changed(void* ctx, event_t* e) {
prop_change_event_t* evt = prop_change_event_cast(e);
log_debug("%s changed\n", evt->name);
return RET_OK;
}
...
app_conf_on_changed(app_conf_changed, NULL);
```
> 回调函数在修改配置的线程调用。如果是后台线程修改配置,在配置变化时,需要更新界面,此时需要用 idle_queue 进行串行化。
## 6. 释放配置相关资源。
```c
/**
* @method app_conf_deinit
* 释放 conf 对象。
*
* @annotation ["static", "scriptable"]
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_deinit(void);
```
示例:
```c
ret_t application_exit() {
app_conf_deinit();
log_debug("application_exit\n");
return RET_OK;
}
```
## 7. key 的规则
* 配置信息通常都是层次结构的为了能访问所有数据key 也必须是多级的,各级的名称之间用“.”分隔。
> “.”不能作为名称的一部分,如果出现,需要用其它字符代替。
```json
{
"network" : {
"eth0" : {
"ip" : "192.169.0.100",
"gateway" : "192.169.0.1",
"mask" : "255.255.255.0"
}
}
}
```
在这个例子中,要访问 ip 的这个配置,可以用"network.eth0.ip":
```c
app_conf_set_str("network.eth0.ip", "192.169.0.100");
```
* 按索引访问
在访问时,每一级的名称,可以用索引代替,格式为:[+索引+],比如 [0] 表示第一项,[2] 表示第三项。这对于不清楚名称的情况,或遍历全部配置,很有帮助。
如:
```c
const char* ip0 = app_conf_get_str("network.[0].ip", NULL);
const char* ip1 = app_conf_get_str("network.[1].ip", NULL);
const char* ip2 = app_conf_get_str("network.[2].ip", NULL);
```
*#size 获取子节点个数
示例:
```ini
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
```
在上面的例子中,"#size"获取分组的个数为 2"wifi.#size"获取 wifi 下的子项数为 2。
```c
assert(app_conf_get_int("#size", 0) == 2);
assert(app_conf_get_int("wifi.#size", 0) == 2);
```
*#name 获取项的名称。
示例:
```ini
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
```
在上面的例子中,"[0].#name" 获取第一个分组的名称为"wifi""wifi.[0].#name" 获取 wifi 下第一项的名称为 "on"。
```c
assert(strcmp(app_conf_get_str("[0].#name", NULL), "wifi") == 0);
assert(strcmp(app_conf_get_str("wifi.[0].#name", NULL), "on") == 0);
```
*#index 获取配置项在兄弟节点中的序数。
```ini
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
```
在上面的例子中,"wifi.#index" 获取 wifi 的序数为 0"server.#index" 获取 server 的序数为 1。
```c
assert(app_conf_get_int("wifi.#index", -1) == 0);
assert(app_conf_get_int("server.#index", -1) == 1);
```
## 8. 缺省配置
* 位置。缺省配置放到资源中位于design/default/data
* 命名:应用程序名 + '.' + 文件扩展名。
* 文件扩展名与初始化时指定的格式一致。
比如,下面的例子中,应用程序名 demo扩展名为 json。所以对应的缺省配置文件为design/default/demo.json
```c
ENSURE(app_conf_init_json("demo") == RET_OK);
```
> 在初始化时,如果配置文件不存在,则将缺省配置拷贝到配置文件。
## 9. 注意事项
* . 作为 key 的分隔符。
* ini 格式不支持注释和换行。
## 10. 参考示例
* [conf_ini](https://github.com/zlgopen/awtk-c-demos/blob/master/demos/conf_ini.c)
* [conf_json](https://github.com/zlgopen/awtk-c-demos/blob/master/demos/conf_json.c)
* [conf_ubjson](https://github.com/zlgopen/awtk-c-demos/blob/master/demos/conf_ubjson.c)