Thread has crashed: A data abort exception has occurred accessing 0x2d
-
@/* header file */
#ifndef _WEIBOIMAGEPROVIDER_H
#define _WEIBOIMAGEPROVIDER_H#include <QDeclarativeImageProvider>
#include <QImage>
#include <QString>
#include <QSize>
#include <QUrl>
#include <QObject>class QNetworkAccessManager;
class WeiboImageProvider:
public QObject, public QDeclarativeImageProvider {
Q_OBJECTpublic:
WeiboImageProvider(QNetworkAccessManager *);
~WeiboImageProvider();
QImage requestImage(const QString&, QSize *, const QSize&);Q_SIGNALS:
void imageRequested(const QUrl&, const QString&);
void imageDone();public slots:
void generateImage(const QUrl&, const QString&);private:
QString locateImage(const QUrl&);private:
QNetworkAccessManager *_accMgr;
};#endif
/* implementation file */
#include "weiboimageprovider.h"#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QIODevice>
#include <QEventLoop>
#include <QDebug>WeiboImageProvider::WeiboImageProvider(QNetworkAccessManager *accessManager):
QObject(0),
QDeclarativeImageProvider(QDeclarativeImageProvider::Image)
{
_accMgr = accessManager;
qDebug("_accMgr addr: %p", _accMgr);
}WeiboImageProvider::~WeiboImageProvider()
{
}QImage WeiboImageProvider::requestImage(const QString& id,
QSize *size, const QSize& requestedSize)
{
if(id.indexOf("http", 0, Qt::CaseInsensitive) != 0)
return QImage();(void)size;
(void)requestedSize;/*
int width = 50, height = 50;if(size) {
if(requestedSize.isEmpty())
*size = QSize(width, height);
else
*size = QSize(requestedSize.width(), requestedSize.height());
}
*/QUrl imageUrl = QUrl(id);
QString imagePath = locateImage(imageUrl);if(QFile::exists(imagePath)) {
if(QFile(imagePath).size() > 0)
return QImage(imagePath);
}//generateImage(imageUrl, imagePath);
emit imageRequested(imageUrl, imagePath);//force to block
QEventLoop eventLoop;
QObject::connect(this, SIGNAL(imageDone()), &eventLoop, SLOT(quit()));
eventLoop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);return QImage(imagePath);
}void WeiboImageProvider::generateImage(const QUrl& imageUrl, const QString& imagePath)
{
qDebug() << "start generateImage";
qDebug() << "image URL: " << imageUrl.toString();QNetworkReply *reply = _accMgr->get(QNetworkRequest(imageUrl));
qDebug("reply addr: %p", reply);qDebug() << "blocking";
//force to block
QEventLoop eventLoop;
QObject::connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);qDebug() << "write file: " << imagePath;
QFile imageFile(imagePath);
imageFile.open(QIODevice::WriteOnly);
imageFile.write(reply->readAll());
imageFile.flush();
imageFile.close();
qDebug() << "write OK: " << imagePath;reply->close();
reply->~QNetworkReply();emit imageDone();
qDebug() << "end generateImage";
}QString WeiboImageProvider::locateImage(const QUrl& id)
{
//http://tp4.sinaimg.cn/1799583891/50/5616916109/1
QString urlPath = id.path().mid(1);
QString userId = urlPath.mid(0, urlPath.indexOf(QChar('/')));QDir imgDir("./qml/weiboimages");
if(!imgDir.exists()) {
qDebug() << "weibo image dir not exists";
imgDir.mkpath("./qml/weiboimages");
}return QFileInfo("./qml/weiboimages/" + userId).absoluteFilePath();
}@ -
@/* in main.cpp file */
WeiboImageProvider *imgProvider = new WeiboImageProvider(viewer.engine()->networkAccessManager());QObject::connect(imgProvider, SIGNAL(imageRequested(const QUrl&, const QString&)), imgProvider, SLOT(generateImage(const QUrl&, const QString&)));
viewer.engine()->addImageProvider("weiboimageprovider", imgProvider);@
-
If you are developing for Symbian platform it could be because of a missing capability which is needed in order to use the networking modules. So if that is your case you should add the following in you .pro file:
@
symbian{
TARGET.CAPABILITY += NetworkServices
}
@Also one other note, is the following code of yours legitimate? Do you really need to delete it?
@
reply->~QNetworkReply();
@If it needs to be deleted then you better do the following:
@
delete reply;
@ -
Yes, it's for the Symbian platform indeed. However, I've been always enabling the NetworkServices capability all the time in my .pro file.
The pointer variable "reply" is returned by QNetworkAccessManager::get(QNetworkRequest&), but not new-ed by manual, so I call the destructor explicitly instead of delete.
-
You must not[1] call the destructor of a class directly. So the following line is forbidden:
@
reply->~QNetworkReply();
@replace ist by
@
delete reply;
@If your code still crashes, then please run in a debugger and tell us where.
Another recommendation:
If a class depends on certain signal slot connections within itself, then make the connections in that class, eg. the constructor or some init method, and do not make the connection somewhere outside:@
// move this to the class
QObject::connect(imgProvider, SIGNAL(imageRequested(const QUrl&, const QString&)), imgProvider, SLOT(generateImage(const QUrl&, const QString&)));// and change to
connect(this, SIGNAL(imageRequested(QUrl, QString)), this, SLOT(generateImage(QUrl, QString)));
@fn1. There are rare situations (e.g. placement new) where it's ok to call the destructor. See the "C++ FAQs":http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.6 for details.
-
Volker, thanks a lot.
I've run the GDB debugger integrated with Qt creator, but it told nothing related to the crash (such as where the segment fault happend) but the literal error information.
Moreover, would you kindly enough tell in detail why it is better to make the connection in the constructor?
-
Then I recommend running in the debugger and execute the code line by line. When it eventually crashes, you see where.
Because it's an internal implementation detail. Those should go into the class. And your class works without anyone outside needing to know that this connection has to be setup.
BTW:
You have two blocking event loops in your code. From how you use the code, I would say one is not necessary and it should be sufficient to call generateCode directly, not via a signal/slot. -
Just as you said, at the very beginning, I called generateImage (you mentioned as generateCode) directly instead of making use of signal/slot. However, if I did so, I always get the following error:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x*****), parent's thread is QThread(0x******), current thread is ResultThread(0x******)And then, I use signal/slot to avoid that error.
-
You did read this "wiki article":wiki/Threads_Events_QObjects already, didn't you?
There's nothing to comment on the actual problem, your complete threading code is missing.
And as a first advice:
Think very hard if you really need threads in the first place. QNAM is asynchronous already.