14 KiB
如何存取应用程序的配置信息
1. 介绍
AWTK 提供了 app_conf 接口,用于保存或读取应用程序的配置信息。其特点有:
- 线程安全,可以多线程访问。
- 观察者模式,配置变化时通知关注者。
- 统一接口,不用关注存储格式和存储介质。
- 接口支持多种数据类型,开发者无需在字符串和其它类型之间转换。
配置信息可以保存为任意格式,目前支持下列文件格式(其它格式开发者可以自行增加):
配置信息可以存放在任何介质上,目前支持以下介质:
-
文件系统。
-
Flash(TODO)
为了方便软件升级时,保留用户配置,先从用户配置读取,如果配置不存在,再读取默认配置。
2. 初始化(任意选一种格式即可)
app_conf 作为可选组件,需要开发者自己初始化。
初始化函数一般在 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 格式时的初始方法
包含头文件:
#include "conf_io/app_conf_init_ini.h"
调用初始化函数
/**
* @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)
示例:
ENSURE(app_conf_init_ini("demo") == RET_OK);
- 2.2 使用 json 格式时的初始方法
包含头文件:
#include "conf_io/app_conf_init_json.h"
调用初始化函数
/**
* @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)
示例:
ENSURE(app_conf_init_json("demo") == RET_OK);
- 2.3 使用 ubjson 格式时的初始方法
包含头文件:
#include "conf_io/app_conf_init_ubjson.h"
调用初始化函数
/**
* @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)
示例:
ENSURE(app_conf_init_ubjson("demo") == RET_OK);
3. 存取配置信息
3.1 包含头文件:
app_conf 作为可选组件,需要开发者自己包含头文件。
#include "conf_io/app_conf.h"
3.2 修改配置信息
/**
* @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);
示例:
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 格式时,实际存为:
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
使用 json 格式时,实际存为:
{
"wifi" : {
"on" : true,
"name" : "awtk"
},
"server" : {
"port" : 8080,
"timeout" : 1
}
}
使用 ubjson 格式时,实际存为二进制格式,这里不再举例。
3.3 读取配置信息
/**
* @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);
示例:
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 删除配置信息
/**
* @method app_conf_remove
* 删除配置项。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_remove(const char* key);
示例:
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 检查配置信息是否存在
/**
* @method app_conf_exist
* 检查配置项是否存在。
*
* @annotation ["static", "scriptable"]
*
* @param {const char*} key 配置项的名称。
*
* @return {bool_t} 返回 TRUE 表示存在,FALSE 表示不存在。
*/
bool_t app_conf_exist(const char* key);
示例:
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 持久保存配置信息
/**
* @method app_conf_save
* 持久保存配置。
*
* @annotation ["static", "scriptable"]
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_save(void);
示例:
app_conf_save();
3.7 关注配置信息的变化
/**
* @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);
示例:
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 进行串行化。
3.8 恢复出厂设置
删除用户配置文件,并重新加载。
/**
* @method app_conf_reset
*
* 恢复出厂设置。
*
* @annotation ["global"]
*
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
*/
ret_t app_conf_reset(void);
6. 释放配置相关资源。
/**
* @method app_conf_deinit
* 释放 conf 对象。
*
* @annotation ["static", "scriptable"]
*
* @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
*/
ret_t app_conf_deinit(void);
示例:
ret_t application_exit() {
app_conf_deinit();
log_debug("application_exit\n");
return RET_OK;
}
7. key 的规则
- 配置信息通常都是层次结构的,为了能访问所有数据,key 也必须是多级的,各级的名称之间用“.”分隔。
“.”不能作为名称的一部分,如果出现,需要用其它字符代替。
{
"network" : {
"eth0" : {
"ip" : "192.169.0.100",
"gateway" : "192.169.0.1",
"mask" : "255.255.255.0"
}
}
}
在这个例子中,要访问 ip 的这个配置,可以用"network.eth0.ip":
app_conf_set_str("network.eth0.ip", "192.169.0.100");
- 按索引访问
在访问时,每一级的名称,可以用索引代替,格式为:[+索引+],比如 [0] 表示第一项,[2] 表示第三项。这对于不清楚名称的情况,或遍历全部配置,很有帮助。
如:
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 获取子节点个数
示例:
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
在上面的例子中,"#size"获取分组的个数为 2,"wifi.#size"获取 wifi 下的子项数为 2。
assert(app_conf_get_int("#size", 0) == 2);
assert(app_conf_get_int("wifi.#size", 0) == 2);
- 用 #name 获取项的名称。
示例:
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
在上面的例子中,"[0].#name" 获取第一个分组的名称为"wifi","wifi.[0].#name" 获取 wifi 下第一项的名称为 "on"。
assert(strcmp(app_conf_get_str("[0].#name", NULL), "wifi") == 0);
assert(strcmp(app_conf_get_str("wifi.[0].#name", NULL), "on") == 0);
- 用 #index 获取配置项在兄弟节点中的序数。
[wifi]
on = 1
name = awtk
[server]
port = 8080
timeout = 1.000000
在上面的例子中,"wifi.#index" 获取 wifi 的序数为 0,"server.#index" 获取 server 的序数为 1。
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
ENSURE(app_conf_init_json("demo") == RET_OK);
优先读取用户配置,如果没有读到,再读取默认配置。
9. 注意事项
-
英文的点(.) 作为 key 的分隔符,所以 key 不能包含英文的点(.)。
-
ini 格式不支持注释和换行。