QML 从无到有 3 (自动更新)
新的需求出来啦,需要自动更新功能,不怕程序升级了。
自动更新,QML不好写,需要c++来辅助,这里就涉及QML中调用c++功能(这里就不写了,百度一下,很多)。
思路:获取版本》下载程序》安装程序
第一步,获取版本信息,可以通过XMLHttpRequest获取最新版本。
第二步,下载程序,通过Qt c++的QtNetwork:
头文件:
1 #ifndef ZRDOWNLOAD_H 2 #define ZRDOWNLOAD_H 3 4 #include <QObject> 5 #include <QtNetwork/QNetworkAccessManager> 6 #include <QFile> 7 #include <QtNetwork/QNetworkReply> 8 #include <QtNetwork/QNetworkRequest> 9 #include <QIODevice> 10 11 class ZRDownload : public QObject 12 { 13 Q_OBJECT 14 Q_PROPERTY(QString url READ GetUrl WRITE SetUrl) 15 Q_PROPERTY(QString fileName READ GetFileName WRITE SetFileName) 16 public: 17 explicit ZRDownload(QObject *parent = 0); 18 ~ZRDownload(); 19 20 QString GetUrl(); 21 void SetUrl(QString u); 22 QString GetFileName(); 23 void SetFileName(QString fn); 24 private: 25 QString url; 26 QString fileName; 27 QFile *file; 28 QNetworkAccessManager *accessManager; 29 QNetworkRequest request; 30 QNetworkReply *reply; 31 32 signals: 33 void progressPosition(double pre); //更新进度条信号 34 void downloadFinished(); //结束信号 35 36 protected slots: 37 void httpDownload(); 38 void replyFinished(QNetworkReply*reply); 39 void onDownloadProgress(qint64 bytesSent,qint64 bytesTotal); 40 void onReadyRead(); 41 }; 42 43 #endif // ZRDOWNLOAD_H
CPP文件:
#include "zrdownload.h" #include <QDebug> ZRDownload::ZRDownload(QObject *parent) : QObject(parent) { accessManager = NULL; reply = NULL; } ZRDownload::~ZRDownload() { } QString ZRDownload::GetUrl() { return url; } void ZRDownload::SetUrl(QString u) { url = u; } QString ZRDownload::GetFileName() { return fileName; } void ZRDownload::SetFileName(QString fn) { fileName = fn; } void ZRDownload::httpDownload(){ file=new QFile(fileName); bool suc = file->open(QIODevice::WriteOnly);//只读方式打开文件 accessManager=new QNetworkAccessManager(this); request.setUrl(url); /******************设置http的header***********************/ // request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data"); // request.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream"); // request.setRawHeader("Content-Disposition","form-data;name='doc';filename='a.txt'"); //request.setHeader(QNetworkRequest::ContentLengthHeader,post_data.length()); reply=accessManager->get(request);//通过发送数据,返回值保存在reply指针里. connect(accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));//finish为manager自带的信号,replyFinished是自定义的 connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT( onDownloadProgress(qint64 ,qint64 )));//download文件时进度 connect((QObject *)reply, SIGNAL(readyRead()),this, SLOT(onReadyRead())); } /***************响应结束**************************/ void ZRDownload::replyFinished(QNetworkReply *reply){ //获取响应的信息,状态码为200表示正常 QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); QByteArray bytes = reply->readAll(); //获取字节 QString result(bytes); //转化为字符串 //无错误返回 if(reply->error() == QNetworkReply::NoError) { QByteArray bytes = reply->readAll(); //获取字节 QString result(bytes); //转化为字符串 file->flush(); file->close(); delete file; file=NULL; } else { //处理错误 } reply->deleteLater();//要删除reply,但是不能在repyfinished里直接delete,要调用deletelater; reply=NULL; accessManager->deleteLater(); accessManager=NULL; downloadFinished(); } /***********更新进度条*************/ void ZRDownload::onDownloadProgress(qint64 bytesSent, qint64 bytesTotal){ double pre = double(bytesSent)/bytesTotal; progressPosition(pre); } void ZRDownload::onReadyRead(){ file->write(reply->readAll()); }
上面的代码也是修改网上的代码,增加了QML调用的信息。
注意:项目文件pro中需要添加 QT += network
main.cpp:
添加 qmlRegisterType<ZRDownload>("xxx.xxx.ZRDownload", 1, 0, "ZRDownload");
QML调用:
1 ZRDownload{ 2 id:download 3 url:"http://xxx.xxx.com/xx.apk" 4 fileName:"" 5 onProgressPosition:progressBar.update(pre) //自定义更新进度条函数 6 onDownloadFinished: { 7 //下载完成 8 } 9 } 10 11 //启动下载 12 download.fileName=getAppPath();//设置保存目录,pc和安卓不同 13 download.httpDownload();
第三步,安装程序:
不同平台,安装程序的代码也不同,PC平台使用QProcess启动安装程序,安卓平台通过QAndroidJniObject调用Activity实现。下面是代码:
1 #ifndef ZRINSTALL_H 2 #define ZRINSTALL_H 3 4 #include <QObject> 5 #include <QString> 6 7 class ZRInstall : public QObject 8 { 9 Q_OBJECT 10 Q_PROPERTY(QString fileName WRITE SetFileName) 11 public: 12 explicit ZRInstall(QObject *parent = 0); 13 14 void SetFileName(QString fn); 15 signals: 16 17 public slots: 18 void install(); 19 private: 20 QString m_FileName; 21 }; 22 23 #endif // ZRINSTALL_H
1 #include "zrinstall.h" 2 #include <QDebug> 3 #if defined(Q_OS_ANDROID) 4 #include <QtAndroid> 5 #else 6 #include <QProcess> 7 #endif 8 9 ZRInstall::ZRInstall(QObject *parent) : QObject(parent) 10 { 11 12 } 13 14 void ZRInstall::SetFileName(QString fn) 15 { 16 m_FileName = fn; 17 } 18 19 void ZRInstall::install() 20 { 21 #if defined(Q_OS_ANDROID) 22 //安装apk 23 QAndroidJniObject path = QAndroidJniObject::fromString("file://"+m_FileName); 24 QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", path.object<jstring>()); 25 QAndroidJniObject param = QAndroidJniObject::getStaticObjectField<jstring>("android/content/Intent", "ACTION_VIEW"); 26 QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V",param.object<jstring>()); 27 QAndroidJniObject type = QAndroidJniObject::fromString("application/vnd.android.package-archive"); 28 QAndroidJniObject result = intent.callObjectMethod("setDataAndTypeAndNormalize", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;", uri.object<jobject>(), type.object<jstring>()); 29 QtAndroid::startActivity(intent, 1001, 0); 30 #else 31 //qDebug()<<m_FileName; 32 QProcess * proc = new QProcess(); 33 proc->startDetached(m_FileName,QStringList()); 34 delete proc; 35 #endif 36 }
1 android{ 2 QT += androidextras 3 }
调用就不写了,可以参考上面download。
这样整个版本自动更新就写好了,有一些细节自己需要注意,如下载安装文件不同,安装目录不同。