消息批量推送工具 WePush 发布 5.0.6。这个版本没有堆叠新的业务功能,而是把改进重点放在运行稳定性上:分析并优化界面 EDT 线程问题、提升人群数据导入性能、修复 SQLite 不支持 useGeneratedKeys 的问题,同时调整自动更新和安装包发布流程,并升级部分依赖。
对于需要导入大量联系人、长时间执行批量任务的桌面工具来说,这类维护版本往往比新增按钮更有价值。界面是否会卡住、数据库写入能否稳定完成、升级过程是否可靠,直接决定工具能不能用于日常生产任务。
EDT 优化为什么会直接影响使用体验
EDT(Event Dispatch Thread,事件分派线程)负责处理 Java 桌面界面的点击、绘制和状态更新。如果人群文件解析、数据校验或数据库写入也在 EDT 中同步执行,任务持续几秒后,窗口就可能无法拖动,按钮也无法响应。
WePush 5.0.6 明确提到分析并优化 EDT 线程问题,目标是提高 UI 响应稳定性。通常,这意味着需要把耗时操作放到后台线程,只把进度和结果更新交回 EDT。需要注意的是,版本摘要没有披露具体类和实现方式,因此不能据此断言内部采用了某一种并发组件。
开发类似导入功能时,可以这样实践。下面的示例可直接编译运行:点击按钮后,耗时任务在 SwingWorker 后台线程执行,界面仍然可以响应。
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class ImportDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(ImportDemo::createUi);
}
private static void createUi() {
JFrame frame = new JFrame("Audience Import Demo");
JButton importButton = new JButton("Import audience");
JProgressBar progress = new JProgressBar(0, 100);
progress.setStringPainted(true);
importButton.addActionListener(event -> {
importButton.setEnabled(false);
SwingWorker<Integer, Integer> worker = new SwingWorker<>() {
@Override
protected Integer doInBackground() throws Exception {
for (int i = 1; i <= 100; i++) {
Thread.sleep(30); // Replace with parsing or database work.
publish(i);
}
return 100;
}
@Override
protected void process(List<Integer> values) {
progress.setValue(values.get(values.size() - 1));
}
@Override
protected void done() {
importButton.setEnabled(true);
try {
JOptionPane.showMessageDialog(frame,
"Imported " + get() + "% successfully");
} catch (Exception exception) {
JOptionPane.showMessageDialog(frame,
exception.getMessage(), "Import failed",
JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
});
frame.setLayout(new BorderLayout(8, 8));
frame.add(importButton, BorderLayout.NORTH);
frame.add(progress, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(420, 130);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
将代码保存为 ImportDemo.java 后运行:
javac ImportDemo.java
java ImportDemo
实际项目还需要处理取消操作、线程池容量、重复点击和窗口关闭等情况。后台线程也不能直接修改 Swing 组件,应通过 process、done 或 SwingUtilities.invokeLater 回到 EDT。
人群导入优化不只是“读取更快”
批量推送的前置步骤通常是导入人群数据。这个过程可能同时涉及 CSV 或表格解析、字段清洗、号码或账号校验、去重,以及 SQLite 批量写入。任何一个环节逐行同步处理,都可能放大总体耗时。
5.0.6 对人群数据导入等场景进行了性能优化,但摘要没有给出基准数据、文件规模和内存变化,因此升级后应使用自己的真实数据验证效果。建议至少记录以下指标:
- 导入 1 万、10 万条记录分别需要多长时间;
- 导入期间界面能否持续响应;
- 峰值内存是否明显增长;
- 重复数据、空字段和错误编码是否得到一致处理;
- 导入失败后是否会留下部分写入的数据。
如果自行实现 SQLite 导入,可以这样实践:使用事务和批处理,避免每插入一条记录就提交一次。
BEGIN TRANSACTION;
INSERT INTO audience(name, target) VALUES ('Alice', 'alice@example.com');
INSERT INTO audience(name, target) VALUES ('Bob', 'bob@example.com');
INSERT INTO audience(name, target) VALUES ('Carol', 'carol@example.com');
COMMIT;
生产代码中应使用参数化语句和 JDBC addBatch(),不要通过字符串拼接用户数据。批次也不宜无限增大,可以按 500 至 2000 条分批执行,再根据数据量、磁盘和内存测试调整。
SQLite 的生成主键兼容问题
本次更新修复了 SQLite 不支持 useGeneratedKeys 的问题。useGeneratedKeys 常见于数据库连接或 ORM 配置,用于在插入数据后读取数据库生成的主键;但不同 JDBC 驱动和数据库对该能力的支持并不完全一致。若通用数据库配置未经区分地应用到 SQLite,可能造成插入异常或主键读取失败。
版本摘要没有说明 WePush 的具体修复方式。对于其他项目,可以这样排查:
- 检查连接池、ORM 或数据访问层是否全局启用了
useGeneratedKeys。 - 确认当前 SQLite JDBC 驱动版本及其生成键支持方式。
- 为 SQLite 单独配置主键获取逻辑,不要直接复用 MySQL 等数据库的配置。
- 增加真实 SQLite 文件上的集成测试,覆盖单条插入、批量插入和事务回滚。
升级依赖同样需要关注兼容边界。不要只验证应用能否启动,还应测试历史数据库能否打开、已有配置能否读取,以及消息推送任务能否从导入一直执行到完成。
升级前后的检查清单
WePush 5.0.6 还优化了自动更新下载和安装包发布流程。由于批量推送工具通常包含联系人、发送配置和历史任务,升级前仍建议备份工作目录与数据库文件,并记录当前版本,避免出现问题时无法定位数据来源。
可以按以下顺序验收:
- 备份 SQLite 数据库、联系人文件和必要配置;
- 在非关键任务时间安装新版本;
- 使用一份小规模测试人群验证导入、去重和字段映射;
- 执行一次受控的小批量推送,检查渠道配置和发送结果;
- 再用接近真实规模的数据观察 UI 响应、导入耗时和内存占用;
- 确认自动更新不会覆盖业务数据或自定义配置。
5.0.6 的价值集中在稳定性、性能和兼容性,而不是可见的新功能。经常处理大规模人群数据,或曾遇到界面无响应、SQLite 插入异常的用户,更值得优先评估升级;但涉及重要推送任务时,仍应先备份并完成小流量验证。