PDA

View Full Version : QNetworkReply::readAll() causes app to crash



Urthas
23rd June 2017, 01:39
My app queues a series of files to download, and downloads each one in sequence. That is, it calls QNetworkAccessManager::get(), checks for errors in onFinished(), and if there are none, saves the data to disk. There are problems with this overall approach -- as opposed to writing the data as it arrives -- in that if a given file is too large, it will fill up the memory on a mobile device and cause the OS to kill the app.

That's not what I am seeing here.

And in fact, I am only seeing this for a particular file on a particular device, which happens to be a Nexus 7 running Android 5.1. Works fine on every iOS device I've tried, and a Nexus 7 running Android 6.0.1. The file downloads just fine, and there are no errors. But when I try to call readAll() on my non-null instance of QNetworkReply:


F/libc (23481): Fatal signal 6 (SIGABRT), code -6 in tid 23524 (QtThread)
I/DEBUG ( 182): pid: 23481, tid: 23524, name: QtThread >>> my.org.appname <<<
E/lowmemorykiller( 176): Error writing /proc/23481/oom_score_adj; errno=22
I/Zygote ( 195): Process 23481 exited due to signal (6)
I/ActivityManager( 543): Process my.org.appname (pid 23481) has died

Similar topic: http://www.qtcentre.org/threads/66848-QNetworkReply-readAll-error

The author resolved it, but the solution was apparently so self-evident that no details were given.

My function is as follows:


bool DownloadManager::saveToDisk(const QString &fileName, QIODevice *data) {
QFile file(fileName);
bool ok = file.open(QIODevice::WriteOnly);
if (ok) {
QByteArray stuff = data->readAll(); // BOOM!
file.write(stuff);
file.close();
}
else
qDebug() << QString("DownloadManager::saveToDisk(%1) - Failed: %2").arg(fileName).arg(file.errorString());
return ok;
}

And here is the calling code:


void DownloadManager::onFinished() {
const QUrl urlObj = m_reply->url();
const QString url = urlObj.toString();
const QNetworkReply::NetworkError err = m_reply->error();
if (err) {
qDebug() << QString("DownloadManager::onFinished(%1) - Error: %2").arg(url).arg(m_reply->errorString());
emit failure(url, (err == QNetworkReply::OperationCanceledError ? CanceledByUser : Network));
}
else {
const QString fileName = absoluteSaveFileName(urlObj);
if (saveToDisk(fileName, m_reply))
emit success(url, fileName);
else {
qDebug() << QString("DownloadManager::onFinished(%1) - Failed to save to '%2'").arg(url).arg(fileName);
emit failure(url, FileIO);
}
}
m_reply->deleteLater();
if (m_queue.isEmpty())
setIsDownloading(false);
else {
m_reply = m_manager.get(m_queue.takeFirst());
Q_ASSERT_X(m_reply, "onFinished", "Null pointer to QNetworkReply!");
#ifndef QT_NO_SSL
connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(sslErrors(QList<QSslError>)));
#endif
}
}

The cited topic and error message certainly make this look like a threading issue, but I need a hand with understanding and resolving it. I should add that I'm using Qt Creator 4.2.2 based on Qt 5.8.0.

Thanks in advance.

Urthas
26th June 2017, 19:52
The working solution, for the moment (i.e., as opposed to writing the data as it arrives), involves appending the fully downloaded data to the output file a little at a time:



bool DownloadManager::saveToDisk(const QString &fileName, QIODevice *inputDevice) {
QFile file(fileName);
bool ok = file.open(QIODevice::Append);
if (ok) {
const qint64 MAX_LEN = 1024; // arbitrary...sort of
while (ok && !inputDevice->atEnd())
ok = file.write(inputDevice->read(MAX_LEN)) != -1;
file.close();
}
return ok;
}


Clearly the original problem was related to the file size, which at first glance doesn't seem to have much to do with threading. I'm frankly a bit annoyed at Qt, or Android, or both! :P