Saturday, December 4, 2010

Singleton Download Queue for Qt

Every now and then, I need to use the QNetworkAccessManager class in my applications, and have some synchronization problems every now and then.

So, I used the same behavior I find in the fast food restaurant. I make an order and take a ticket with a number. When an order is ready, they call for the number of it's ticket. If it is mine, I go and get it.

Here are the important methods:
[DownloadQueue]
public:
    int         addUrl(QUrl url);
    bool        removeUrl(int index);

    QByteArray  getData(int index);
    bool        removeData(int index);

    int         getCurrentIndex();
    bool        clear();

signals:
    void downloadFinished(int index);

When I want to make a new download, I add the url to the DownloadQueue class and get an index to the added url. I can remove the url if I need, using the same index.

Whenever a download is finished, the class emits a signal with its index, so I should make a slot to listen for these 'announcements'. If the announced index is the one I ordered, I can get its ready data by using getData() method.

Beware that the received data resides in the memory until you remove it, even though you called getData().

Another method is for checking the current processed index, just to know if my order is taking long or going to be next...etc. And the clear method to empty all the queue.

Another twist is made to make the same queue accessible by all classes. So the class is a singleton. And an instance is made like this
//example
DownloadQueue *d = DownloadQueue::getInstance();
Thanks to Mahmoud Mamdouh for helping me with the singleton thing.

Here is the complete class:
(Download)

[DownloadQueue.h]
#ifndef DOWNLOADQUEUE_H
#define DOWNLOADQUEUE_H

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
#include <QTimer>
#include <QUrl>

class DownloadQueue : public QObject
{
    Q_OBJECT

public:
    static DownloadQueue* getInstance();

    int         addUrl(QUrl url);
    bool        removeUrl(int index);

    QByteArray  getData(int index);
    bool        removeData(int index);

    int         getCurrentIndex();
    bool        clear();

private:
    DownloadQueue(QObject *parent = 0);
    static DownloadQueue *_instance;
    int _index;
    QTimer _timer;
    QUrl _url;
    QList<QUrl> _urlList;
    QList<QByteArray> _dataList;
    QNetworkAccessManager _manager;

signals:
    void downloadFinished(int index);

private slots:
    void handleNetworkReply(QNetworkReply *reply);
    void downloadNext();

};

#endif // DOWNLOADQUEUE_H

[DownloadQueue.cpp]
#include "downloadqueue.h"

DownloadQueue * DownloadQueue::_instance = 0;

DownloadQueue* DownloadQueue::getInstance()
{
    if(_instance == 0) _instance = new DownloadQueue();
    return _instance;
}

DownloadQueue::DownloadQueue(QObject *parent):
        QObject(parent)
{
    _urlList.clear();
    _dataList.clear();
    connect(&_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleNetworkReply(QNetworkReply*)));

    _index = 0;

    connect(&_timer, SIGNAL(timeout()), this, SLOT(downloadNext()));
    _timer.setInterval(500);
    _timer.start();
}

int DownloadQueue::addUrl(QUrl url)
{
    _urlList << url;
    QByteArray data;
    _dataList << data;
    return _urlList.length()-1;
}

bool DownloadQueue::removeUrl(int index)
{
    if((index < _urlList.length()) || (index != _index))
    {
        _urlList.replace(index, QUrl(""));
        return true;
    }

    return false;
}

QByteArray DownloadQueue::getData(int index)
{
    QByteArray data;
    if(index < _dataList.length())
        data = _dataList.at(index);

    return data;
}

bool DownloadQueue::removeData(int index)
{
    if(index < _dataList.length())
    {
        QByteArray data;
        _dataList.replace(index, data);
        return true;
    }

    return false;
}

int DownloadQueue::getCurrentIndex()
{
    return _index;
}

bool DownloadQueue::clear()
{
    _timer.stop();
    _urlList.clear();
    _dataList.clear();
    return true;
}

void DownloadQueue::handleNetworkReply(QNetworkReply *reply)
{
    QByteArray data = reply->readAll();
    _dataList.replace(_index, data);

    emit downloadFinished(_index);
    _index++;
    _timer.start();
}

void DownloadQueue::downloadNext()
{
    if(_index < _urlList.length())
    {
        _timer.stop();
        _manager.get(QNetworkRequest(_urlList.at(_index)));
    }
}

Here is a dummy example to show the basic use of the class.(click me)


Creative Commons License
Blog Example by Mahmoud Adly Ezzat is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

No comments:

Post a Comment