一、项目背景
在软件开发过程中,版本更新检测功能是非常重要的。通过实时获取服务器上的最新版本信息,软件能够及时提醒用户进行升级。这里以Qt开发框架为基础,展示了一个简单的自动更新检测功能的实现。
二、主要技术点
在本文中,我们主要探讨了几个关键的Qt技术点:
- Qt网络模块 (QtNetwork):这个模块帮助我们实现与服务器的通信,从而获取最新版本的更新信息。
- JSON解析模块 (QJsonDocument, QJsonObject):通过这个模块,我们可以解析从服务器获取的JSON格式数据,提取出所需的版本号和下载链接等信息。
- QMessageBox:这个工具用于向用户展示更新提示的对话框,确保用户能够及时了解并选择是否进行更新。
三、代码实现
#ifndef XMAINWINDOW_H
#define XMAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>
#include <QDesktopServices>
#include <QFile>
QT_BEGIN_NAMESPACE
namespace Ui {
class xMainWindow;
}
QT_END_NAMESPACE
class xMainWindow : public QMainWindow
{
Q_OBJECT
public:
xMainWindow(QWidget *parent = nullptr);
~xMainWindow();
public:
int parseUpdateJSON(QString str); // 解析JSON数据
QString getLocalVersion(const QString &filePath); // 获取本地版本
bool saveLocalVersionFile(const QString &jsonStr, const QString &filePath); // 保存版本文件
void onNetworkReplyFinished(QNetworkReply *reply); //网络数据接收
private slots:
void on_pushButton_check_clicked(); // button
private:
Ui::xMainWindow *ui;
QNetworkAccessManager *manager; // 网络请求对象
QString m_localVersion; // 本地版本
};
#endif // XMAINWINDOW_H
#include "xmainwindow.h"
#include "ui_xmainwindow.h"
xMainWindow::xMainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::xMainWindow)
{
ui->setupUi(this);
manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &xMainWindow::onNetworkReplyFinished);
// 获取本地版本号
QString localVersionFilePath = QApplication::applicationDirPath() + "/software_update.json";
m_localVersion = getLocalVersion(localVersionFilePath);
}
xMainWindow::~xMainWindow()
{
delete ui;
}
int xMainWindow::parseUpdateJSON(QString str)
{
QJsonParseError err_rpt;
QJsonDocument root_Doc = QJsonDocument::fromJson(str.toUtf8(), &err_rpt); // 字符串格式化为JSON
if (err_rpt.error != QJsonParseError::NoError)
{
QMessageBox::critical(this, "检查失败", "服务器地址错误或JSON格式错误!");
return -1;
}
if (root_Doc.isObject())
{
QJsonObject root_Obj = root_Doc.object();
QJsonObject PulseValue = root_Obj.value("PulseVersion").toObject();
QString Verison = PulseValue.value("LatestVerison").toString();
QString Url = PulseValue.value("Url").toString();
QString UpdateTime = PulseValue.value("UpdateTime").toString();
QString ReleaseNote = PulseValue.value("ReleaseNote").toString();
qDebug() << "Verison:" << Verison;
if (Verison > m_localVersion)
{
QString warningStr = "检测到新版本!\n版本号:" + Verison + "\n更新时间:" + UpdateTime + "\n更新说明:" + ReleaseNote;
int ret = QMessageBox::warning(this, "检查更新", warningStr, "下载更新", "跳过");
if (ret == 0) //点击更新
{
QDesktopServices::openUrl(QUrl(Url));
// 将从服务器获取的 JSON 数据覆盖写入本地文件
QString localVersionFilePath = QApplication::applicationDirPath() + "/software_update.json";
if (saveLocalVersionFile(str, localVersionFilePath)) {
qDebug() << "本地版本文件已更新.";
m_localVersion = Verison; // 更新本地版本号
} else {
qWarning() << "本地版本文件更新失败.";
}
}
}
else
{
QMessageBox::information(this, "检查更新", "当前已经是最新版本!");
}
}
return 0;
}
QString xMainWindow::getLocalVersion(const QString &filePath)
{
// 打开文件
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "无法打开文件:" << filePath;
return QString(); // 文件打开失败,返回空字符串
}
// 读取文件内容
QByteArray fileData = file.readAll();
file.close();
// 解析JSON数据
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(fileData, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qWarning() << "JSON解析失败:" << parseError.errorString();
return QString(); // JSON解析失败,返回空字符串
}
// 确保 JSON 是对象格式
if (!jsonDoc.isObject()) {
qWarning() << "JSON格式错误,期望对象格式";
return QString(); // 非对象格式,返回空字符串
}
// 获取 JSON 对象
QJsonObject jsonObj = jsonDoc.object();
// 从对象中提取 PulseVersion 对象
if (!jsonObj.contains("PulseVersion") || !jsonObj["PulseVersion"].isObject()) {
qWarning() << "PulseVersion 不存在或不是对象";
return QString();
}
QJsonObject pulseVersionObj = jsonObj["PulseVersion"].toObject();
// 从 PulseVersion 中提取 LatestVerison
if (!pulseVersionObj.contains("LatestVerison") || !pulseVersionObj["LatestVerison"].isString()) {
qWarning() << "LatestVerison 不存在或不是字符串";
return QString();
}
QString latestVersion = pulseVersionObj["LatestVerison"].toString();
//qDebug() << "localVersion:" << latestVersion;
return latestVersion;
}
bool xMainWindow::saveLocalVersionFile(const QString &jsonStr, const QString &filePath)
{
// 打开文件准备写入
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "无法打开文件写入:" << filePath;
return false;
}
// 写入文件内容
QTextStream out(&file);
out << jsonStr;
file.close();
return true;
}
void xMainWindow::onNetworkReplyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QString responseStr = reply->readAll(); // 获取响应数据
parseUpdateJSON(responseStr);
} else {
QMessageBox::critical(this, "检查失败", "网络请求失败!");
}
reply->deleteLater(); // 清理reply对象
}
void xMainWindow::on_pushButton_check_clicked()
{
QNetworkRequest quest;
quest.setUrl(QUrl("http://192.168.9.58:8090/software_update.json")); // 设置请求的URL
quest.setHeader(QNetworkRequest::UserAgentHeader, "RT-Thread ART");
manager->get(quest);
}
UI设计
这个示例的UI设计非常简洁,只包含一个“检查更新”按钮。在on_pushButton_check_clicked()
槽函数中,当用户点击按钮时,程序会发送网络请求来检测是否有更新。
四、功能流程
用户点击了“检查更新”按钮。
程序随即向服务器发送请求,获取最新的版本信息。
服务器返回的JSON数据被程序解析,判断是否有新版本。
如果有新版本,程序会弹出一个提示框,询问用户是否要下载更新。
五、JSON数据格式
{
"PulseVersion": {
"LatestVerison": "4.0.0.0",
"UpdateTime": "2024-10-06",
"Url": "http://192.168.9.58:8090/CheckVersion.exe",
"ReleaseNote": "\n1.修复了启动时偶尔出现的222崩溃问题.\n2.提高了数据加载的速度,优化了性能"
}
}
通过更新服务器上的JSON文件来控制版本,目前只更新check.exe
,如果需要更新多个文件,可以自行修改parseUpdateJSON
函数。