工控设备初始化配置固化:网络、MQTT、设备参数本地持久化方案
大家好,我是黒漂技术佬。设备注册成功了,接下来就是拉配置。
你有没有遇到过这种情况:设备重启后,MQTT连不上了、门锁参数丢了、视频分辨率变回默认值了... 查来查去发现是配置没存住。
工控设备的配置管理,跟手机APP完全不是一个量级。今天咱们就把配置固化这件事说透。
一、工控设备配置管理的特殊要求
先明确需求,做工控的配置,要求比普通APP高得多:
| 要求 | 说明 |
|---|---|
| 断电不丢 | 突然断电再开机,配置必须完好 |
| 异常可恢复 | 配置文件损坏了,能自动回退到默认值 |
| 支持热更新 | 运行中改配置,不用重启APP就能生效 |
| 版本可追溯 | 知道配置什么时候改的、改了什么 |
| 批量统一管理 | 几百台设备的配置要能后台统一推送 |
一句话总结:配置就是设备的"记忆",记忆不能乱,更不能丢。
二、配置分层设计
我一般把配置分成三层,各司其职:
第一层:出厂默认配置(assets内置)
打包在APK里的默认值,作为兜底:
assets/
└── config/
├── default_mqtt_config.json // MQTT默认配置
├── default_door_config.json // 门锁默认参数
├── default_video_config.json // 视频默认配置
└── default_network_config.json // 网络默认配置
java
// 读取默认配置
public class DefaultConfigLoader {
public static MqttConfig loadDefaultMqttConfig(Context context) {
String json = loadFromAssets(context, "config/default_mqtt_config.json");
return new Gson().fromJson(json, MqttConfig.class);
}
}
第二层:本地持久化配置(内部存储)
注册成功后从云端拉取,存在本地,优先级高于默认配置:
/data/data/com.vending.machine/files/config/
├── mqtt_config.json
├── door_config.json
├── video_config.json
└── config_version.ini // 配置版本号
第三层:内存运行时配置
APP运行时的内存对象,所有模块读这个,改配置也是先改内存再落盘:
java
public class ConfigManager {
private MqttConfig mqttConfig;
private DoorConfig doorConfig;
private VideoConfig videoConfig;
// 单例,全局唯一
private static ConfigManager instance;
}
加载优先级:内存 > 本地持久化 > 默认配置
三、配置持久化的几种方案对比
方案对比表
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| SharedPreferences | 简单方便,API友好 | 大配置效率低,ANR风险,进程间不安全 | 简单开关、小参数 |
| JSON文件 | 结构清晰,可读,易备份 | 写入需要自己处理并发 | 中等大小的结构化配置 |
| SQLite数据库 | 支持查询,事务安全 | 太重,配置没必要 | 大量配置项、需要频繁修改 |
| MMKV | 高性能,多进程安全,腾讯出品 | 引入so库,体积增加 | 推荐!工控设备首选 |
推荐方案:MMKV + JSON文件组合
- 小参数、频繁读写的用 MMKV
- 大的结构化配置用 JSON 文件,加版本号管理
java
// 初始化MMKV
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
String dir = getFilesDir().getAbsolutePath() + "/mmkv";
MMKV.initialize(this, dir);
}
}
java
// 配置管理器核心逻辑
public class ConfigManager {
private static final String KEY_CONFIG_VERSION = "config_version";
private MMKV mmkv;
/**
* 启动时加载配置
*/
public void loadConfig() {
mmkv = MMKV.defaultMMKV();
// 1. 先尝试加载本地配置
MqttConfig localMqtt = loadLocalMqttConfig();
if (localMqtt != null) {
mqttConfig = localMqtt;
} else {
// 2. 本地加载失败,用默认配置
mqttConfig = DefaultConfigLoader.loadDefaultMqttConfig(App.getContext());
// 顺便把默认配置存一份
saveMqttConfig(mqttConfig);
}
}
}
四、配置热更新机制
设备跑着跑着要改配置,总不能让用户重启APP吧?
1. MQTT推送配置更新
用MQTT下发配置更新指令是最常用的方式:
java
// 收到配置更新消息的处理
public void onConfigUpdateMessage(String payload) {
ConfigUpdateMessage msg = new Gson().fromJson(payload, ConfigUpdateMessage.class);
// 1. 校验配置版本,避免降级
if (msg.getVersion() <= getCurrentConfigVersion()) {
Log.w("Config", "配置版本过低,忽略");
return;
}
// 2. 备份当前配置,更新失败可以回滚
backupCurrentConfig();
// 3. 应用新配置
boolean success = applyNewConfig(msg.getConfig());
if (success) {
// 4. 持久化保存
saveConfigToLocal(msg.getConfig(), msg.getVersion());
// 5. 上报更新成功
MqttManager.publishConfigUpdateResult(true, msg.getVersion());
} else {
// 6. 更新失败,回滚
rollbackToBackup();
MqttManager.publishConfigUpdateResult(false, msg.getVersion());
}
}
2. 配置生效策略
不是所有配置都能热生效,分三类处理:
| 配置类型 | 生效方式 | 例子 |
|---|---|---|
| 立即生效 | 改了马上起作用 | 日志级别、心跳间隔、重试次数 |
| 模块重启生效 | 重启对应模块就行 | MQTT地址(断开重连)、视频分辨率(重启录制) |
| 整机重启生效 | 必须重启APP | 串口波特率、系统级参数 |
java
public void applyConfig(ConfigType type, Object config) {
switch (type) {
case LOG_LEVEL:
// 立即生效
Logger.setLevel((LogLevel) config);
break;
case MQTT_CONFIG:
// 重启MQTT模块
MqttManager.getInstance().restart((MqttConfig) config);
break;
case SERIAL_CONFIG:
// 需要重启,标记后下次开机生效
markNeedRebootApply(config);
break;
}
}
五、配置异常自愈机制
工控设备什么奇葩情况都能遇到,配置损坏了怎么办?
1. 配置文件完整性校验
存配置的时候顺便存个MD5,加载的时候校验:
java
public void saveConfigWithChecksum(String configJson) {
String md5 = MD5Utils.getMd5(configJson);
mmkv.encode("mqtt_config_json", configJson);
mmkv.encode("mqtt_config_md5", md5);
}
public String loadConfigWithChecksum() {
String json = mmkv.decodeString("mqtt_config_json");
String savedMd5 = mmkv.decodeString("mqtt_config_md5");
if (TextUtils.isEmpty(json) || TextUtils.isEmpty(savedMd5)) {
return null;
}
String currentMd5 = MD5Utils.getMd5(json);
if (!savedMd5.equals(currentMd5)) {
Log.e("Config", "配置文件校验失败,可能损坏了");
return null;
}
return json;
}
2. 多级降级策略
加载失败了一级一级往回退:
尝试加载本地配置
↓ 失败
尝试加载备份配置
↓ 失败
加载assets默认配置
↓ 失败
用代码硬编码的兜底值
↓
记录错误日志,上报异常
3. 配置自动备份
每次成功更新配置,自动留一个上一版本的备份:
config/
├── mqtt_config.json // 当前生效
├── mqtt_config.bak // 上一版本备份
└── mqtt_config.default // 出厂默认(可选)
六、批量设备配置管理
配置分组策略
几百台设备不可能每台配置都不一样,按分组管理:
- 全局默认配置:所有设备通用
- 型号配置:不同型号的柜子参数不同
- 分组配置:按区域、运营商、客户分
- 设备单独配置:个别设备特殊参数
优先级:单独配置 > 分组配置 > 型号配置 > 全局配置
配置版本管理
后台每发一次配置都升版本号,设备端记录当前版本:
- 方便排查问题:"这台设备配置是哪个版本的?"
- 防止降级:旧版本配置不能覆盖新版本
- 灰度发布:先给几台设备更,没问题再全量
七、踩坑经验
坑1:SP写入导致ANR
- 现象:配置多了之后,偶尔ANR,堆栈在SharedPreferences的commit
- 原因:SP的commit是同步写磁盘,配置大了就卡主线程
- 解决:换MMKV,或者SP用apply异步提交(但apply有丢失风险)
坑2:进程间配置不同步
- 现象:守护进程改了配置,主进程读不到最新的
- 原因:SP多进程不安全,MODE_MULTI_PROCESS也不靠谱
- 解决:用MMKV,或者配置只在主进程管理,其他进程通过IPC获取
坑3:配置更新一半断电
- 现象:更新配置时断电,文件写了一半,启动时解析失败
- 解决:先写临时文件,写完再rename替换原文件;或者用事务性存储
八、总结
工控设备的配置管理,核心是稳 和活:
- 稳:怎么折腾都不丢,坏了能自己恢复
- 活:能远程更新,不用跑现场
三层架构(默认+本地+内存)、MMKV持久化、热更新、异常自愈、版本管理------把这几点做好,配置这块基本就不用操心了。
我是黒漂技术佬,专注无人零售物联网实战。第一篇章的三篇到这里就结束了,下一章咱们进入4G网络专题,聊聊工控设备怎么监听网络状态,解决"假联网"这个老大难问题。
有具体问题欢迎私信交流,看到必回~