天天看點

qt編寫的android菜單,Qt for Android實作與webview的互動

qt編寫的android菜單,Qt for Android實作與webview的互動

8種機械鍵盤軸體對比

本人程式員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎麼選?

在Qt的程式裡搭建一個localhost,實作前端webview+html+js和後端C++的互動,該解決方案能夠跨平台。

Qt要在移動端寫浏覽器隻能用一些比較牽強的方案(如果不自己造輪子的話),就是QtWebView.然而QtWebView提供的接口太少,不友善C++與html互動,如果是寫web應用就比較劣勢,畢竟cordova,react native等的方案非常流行,如果無法前後端互動,純一個靜态頁面實在不好玩,還不如把網頁挂在網際網路上用浏覽器直接通路網頁。在安卓上有一種可行的方案:C++通過jni與java互動,再與js對象互動。這個也有弊端,一個是不是跨平台的方案,第二個是我不會java,隻會c++和js.在網上查到的另一種解決方法是搭建一個local server,通過http請求進行互動。

在GitHub上找到了一些用Qt搭建http server的代碼(當然用boost也可以),例如QtWEbApp,QtWebApp。我最終用的是qhttpserver,star數目很多,比較可惜的是有一陣沒有更新了,但是還是很好的一個東西。

這個程式按照GitHub直接下載下傳下來的項目把代碼編譯,例子可以直接運作在安卓機上。例如運作例子Helloworld,那麼在手機浏覽器上打開http://localhost:8080,可以看到helloworld字樣。按照release模式編譯得到libqhttpserver.so,然後按照文檔在自己的項目裡面添加頭檔案包含和需要連結的庫就可以了。

在Qt建構移動web應用有兩種方案:網頁資源放在qrc檔案裡面,運作時把qrc裡面的内容全部複制到存儲中,或者直接放在assets目錄下(安卓的url格式為:file:///android_asset/site/groundTrackPlot.html,不能直接用assets目錄通路,這個需要注意)。由于QtWebView隻是一個原生浏覽器的wrapper,是以不能直接通路qrc裡面的檔案。現在一種可行的解決方案是後端直接用QFile讀取qrc檔案,作為http請求的響應傳回給前端:

class HelloWorld : public QObject

{

Q_OBJECT

public:

HelloWorld();

private slots:

void handleRequest(QHttpRequest *req, QHttpResponse *resp);

};

HelloWorld::HelloWorld()

{

QHttpServer *server = new QHttpServer(this);

connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),

this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));

server->listen(QHostAddress::Any, 8080);

}

void HelloWorld::handleRequest(QHttpRequest *req, QHttpResponse *resp)

{

Q_UNUSED(req);

QByteArray body = req->path().toLocal8Bit();

qDebug() << req->path();

QFile file(":" + req->path());

if (!file.open(QIODevice::ReadOnly)) {

//return; }

QByteArray data = QByteArray(file.readAll());

qDebug() << data;

resp->setHeader("Content-Length", QString::number(data.size()));

resp->writeHead(200);

resp->end(data);

file.close();

}

在main主函數裡直接調用在QApplication對象建立後就建立HelloWorld(這個http server),就可以實作想要的效果了。速度非常快,傳回的資料量如果特别大那一定是渲染會花更多的時間而不是http請求的過程耗時間。用qrc的一個好處是編譯成obj檔案後占空間更小,而且不會被使用者直接得到,但是編譯會很花時間。

另外有時我們會需要Qt的http request是同步的,比如我們的localhost需要先通路一個外部的資源,再用得到的結果響應請求,那麼代碼如下

class HelloWorld : public QObject

{

Q_OBJECT

public:

HelloWorld();

QByteArray get(const QString&);

QNetworkAccessManager m_qnam;

private slots:

void handleRequest(QHttpRequest *req, QHttpResponse *resp);

};

HelloWorld::HelloWorld() : m_qnam()

{

QHttpServer *server = new QHttpServer(this);

connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),

this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));

server->listen(QHostAddress::Any, 8080);

}

QByteArray HelloWorld::get(const QString &strUrl)

{

assert(!strUrl.isEmpty());

const QUrl url = QUrl::fromUserInput(strUrl);

assert(url.isValid());

QNetworkRequest qnr(url);

qnr.setRawHeader("Origin","a");

QNetworkReply* reply = m_qnam.get(qnr); //m_qnam是QNetworkAccessManager對象

QEventLoop eventLoop;

connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);

eventLoop.exec(QEventLoop::ExcludeUserInputEvents);

QByteArray replyData = reply->readAll();

reply->deleteLater();

reply = nullptr;

return replyData;

}

void HelloWorld::handleRequest(QHttpRequest *req, QHttpResponse *resp)

{

QByteArray body = req->path().toLocal8Bit();

qDebug() << req->path();

QByteArray data = get("some domain" + req->path());

qDebug() << data;

resp->setHeader("Content-Length", QString::number(data.size()));

resp->writeHead(200);

resp->end(data);

}

這樣我們就能夠綜合js,qml,c++的好處,得到一個十分優化的web應用程式。一個神奇的現象是安卓的背景機制,如果我在這個程式裡面開了host,自己通路會很快,但是别的浏覽器通路就會很慢,如果我們此時切回server的那個程式,再切出到外部浏覽器,那麼會發現資源又加載好了,這個的原因應該是Android的背景程式隻會得到很少的性能的配置設定。