1. 程式人生 > >QNetworkAccessManager不使用訊號/槽機制來獲得QNetworkReply的解決辦法

QNetworkAccessManager不使用訊號/槽機制來獲得QNetworkReply的解決辦法

正在做一個第三方微信企業要介面的專案,JAVA的實現方法比較簡單,網上資料也很多。

但是我需要在C++下實現,微信企業號介面大多都是https的GET/POST方法實現的。

Qt提供了一個封裝好的類QNetworkAccessManager,就不需要使用windows的套接字來程式設計了,也脫離的平臺限制。

上圖是我從http://blog.csdn.net/chenlong12580/article/details/7392766 當中借來得,這位兄弟也很好的演示了QNetworkAccessManager的基本呼叫方法。

但是QNetworkAccessManager是為圖形介面而設計的,當Manager得到finished()訊號時,使用一個SLOT函式去做讀取資料的事,這對於顯示在GUI的資料時,可以說不能再好用了。


但是微信企業號在呼叫任何的介面之前,都需要獲得一個認證,就是access_token,需要使用GET方式來獲得,這使得我需要將獲得access_token的方法封裝成一個函式,以便在每次具體的如傳送微信之前,呼叫它。

貼上程式碼:

void MainWindow::getAccessToken()
{
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishedSlot(QNetworkReply*)),Qt::DirectConnection);
    QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(ACCESS_TOKEN_URL)));
}

void MainWindow::finishedSlot(QNetworkReply* reply)
{
    qDebug()<<"start";
    QVariant statusCodeV =
         reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    QVariant redirectionTargetUrl =
         reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if(reply->error()==QNetworkReply::NoError)
    {
        QByteArray bytes = reply->readAll();
        QString rlt =QString::fromUtf8(bytes);
        accessToken = rlt;
    }
    reply->deleteLater();
    //ACCESS_FLAG = true;
    qDebug()<<"finished";
}

以上就是按照官方文件來實現的程式碼片。

如果我再main中直接呼叫

getAccessToken()
的話,程式會直接執行之後的程式碼片,而不會先執行SLOT,因為他們在同一執行緒。

而如果用C++11的 thread.h 用多執行緒呼叫,編譯器也不認QNetworkReply為valid的type,並且多執行緒也不符合程式碼的邏輯,因為主執行緒要等待子執行緒,且只有一個子執行緒。

解決的辦法就是使用QEventLoop:直接上程式碼:

void MainWindow::getAccessToken()
{
    QEventLoop temp_loop;
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(ACCESS_TOKEN_URL)));
    connect(reply, SIGNAL(finished()), &temp_loop, SLOT(quit()));
    temp_loop.exec();
    qDebug()<<"start";
    QVariant statusCodeV =
         reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    QVariant redirectionTargetUrl =
         reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if(reply->error()==QNetworkReply::NoError)
    {
        QByteArray bytes = reply->readAll();
        QString rlt =QString::fromUtf8(bytes);
        accessToken = rlt;
        qDebug()<<accessToken;
    }
    reply->deleteLater();
    qDebug()<<"finished";
}
通過比較之前的程式碼片,我想應該很好看出不同,不僅within一個方法,而且程式碼也更好讀更短,QNetworkAccessManager的操作封裝在一個函式中了。

這裡還要提一點,按照文件的資料,QNetworkReply的waitForReadyRead()函式,應該也可以完成上述操作,但是我發現不管設定幾秒的等待,函式都無腦回false,看網上說Qt在某個版本後這個函式失效了,我用的是Qt5.4,可能4.x下可以用,應該也是可以實現的的。