Mar
09

Учимся скачивать файлы программно в Windows Mobile

Google Buzz

Разработка приложений, использующих в своей работе сетевое взаимодействие или доступ к ресурсам Internet – это довольно популярная штука в наши дни. И в этот раз мы рассмотрим такую часто используемую задачу как загрузка файлов из Internet.

Для того чтобы скачать файл в C++ приложении с wxWinCE надо совсем немного кода. В простейшем случае для реализации однопоточной загрузки файла мы можем использовать класс wxURL, скормив ему адрес загружаемого ресурса.

void MobileDownloaderMainFrame::OnDOWNLOADClick( wxCommandEvent& event )
{
        do
        {
                wxString address = wxGetTextFromUser(_("Input URL"),
                        wxGetTextFromUserPromptStr,
                        wxT("http://wxwidgets.info"));
                if(address.IsEmpty()) break;
                wxURL url(address);
                if(!url.IsOk()) break;
                wxInputStream * stream = url.GetInputStream();
                if(!stream) break;
                wxString result;
                wxStringOutputStream out(&result);
                out.Write(*stream);
                delete stream;
                m_SingleThreadedResultTextCtrl->SetValue(result);
        }
        while(false);
}

Приведенный пример хорош всем кроме того, что пока выполняется загрузка, приложение не отвечает на действия пользователя, т.к. загрузка происходит в основном потоке и пока выполнение функции не закончится, приложение не может продолжить работу.

Чтобы таких ситуаций не случалось можно использовать библиотеку wxDownloadFile, которая позволяет загружать каждый файл в отдельном потоке и получать уведомления о состоянии загрузки.

Наш следующий пример демонстрирует как можно реализовать многопоточную загрузку файлов с минимальными усилиями.

Для начала создадим класс, который будет содержать информацию о загрузке:

#ifndef _DOWNLOAD_INFO_H
#define _DOWNLOAD_INFO_H

#include 
#include 
#include 

class DownloadInfo
{
private:
        wxInt64 m_DownloadedSize;
        wxString m_URL;
        wxDownloadEvent::DownloadSTATUS m_Status;
        wxDownloadFile * m_Downloader;
public:
        DownloadInfo(const wxString & url)
                : m_DownloadedSize(0), m_URL(url),
                m_Status(wxDownloadEvent::DOWNLOAD_NONE), m_Downloader(NULL) {}

        const wxString & GetURL() {return m_URL;}

        wxInt64 GetDownloadedSize() {return m_DownloadedSize;}
        void SetDownloadedSize(wxInt64 value) {m_DownloadedSize = value;}

        wxDownloadEvent::DownloadSTATUS GetStatus() {return m_Status;}
        void SetStatus(wxDownloadEvent::DownloadSTATUS value) {m_Status = value;}

        wxDownloadFile * GetDownloader() {return m_Downloader;}
        void SetDownloader(wxDownloadFile * value) {m_Downloader = value;}
};

WX_DECLARE_STRING_HASH_MAP(DownloadInfo *, DownloadsHash);

#endif

Кроме самого класса с информацией о загрузке нам необходима еще хеш-таблица, которая будет содержать информацию об активных загрузках в приложении. Доступ к элементам хеш-таблицы будет производиться по строковому ключу, в качестве которого мы будем использовать URL.

Для отображения информации о загрузках мы будем использовать компонент wxSimpleHtmlListBox. Компонент будет содержать указатель на хеш-таблицу с загрузками.

Чтобы в списке загрузок отображалась актуальная информация, нам необходимо переопределить метод OnGetItem().

#include "DownloadInfo.h"
...
class wxDownloadListBox: public wxSimpleHtmlListBox
{
...
    DownloadsHash * GetDownloads() const { return m_Downloads ; }
    void SetDownloads(DownloadsHash * value) { m_Downloads = value ; }

    virtual wxString OnGetItem(size_t n) const;

    void AddNew() {SetItemCount(GetItemCount()+1);}

    DownloadsHash * m_Downloads;
};

wxString wxDownloadListBox::OnGetItem(size_t n) const
{
        wxString value;
        do
        {
                DownloadsHash::iterator i = m_Downloads->begin();
                for(size_t j = 0; (j < n) && (i != m_Downloads->end()); j++)
                {
                        i++;
                }
                if(i == m_Downloads->end()) break;
                value = wxString::Format(wxT("%s"), i->first.GetData());
                DownloadInfo * download = i->second;
                if(!download) break;
                switch(download->GetStatus())
                {
                case wxDownloadEvent::DOWNLOAD_INPROGRESS:
                        value += wxString::Format(
                                _(" - downloaded %d bytes"),
                                download->GetDownloadedSize());
                        break;
                case wxDownloadEvent::DOWNLOAD_COMPLETE:
                        value += _(" (finished)");
                        break;
                case wxDownloadEvent::DOWNLOAD_FAIL:
                        value += _(" (failed)");
                        break;
                }
        }
        while (false);
        return value;
}

Метод AddNew() указывает компоненту, что у него добавился новый элемент.

Теперь рассмотрим код, создающий новую загрузку и добавляющий ее в список:

void MobileDownloaderMainFrame::OnADDClick( wxCommandEvent& event )
{
        do
        {
                DownloadParametersDialog * dlg = new DownloadParametersDialog(this);
                int result = dlg->ShowModal();
                wxString url = dlg->m_URLTextCtrl->GetValue();
                wxString fileName = dlg->m_FileNamePicker->GetPath();
                dlg->Destroy();
                if(result != wxID_OK) break;
                DownloadInfo * info = m_Downloads[url];
                bool needAddNew(true);
                if(info != NULL)
                {
                        info->GetDownloader()->CancelDownload();
                        needAddNew = false;
                }
                else
                {
                        info = new DownloadInfo(url);
                }
                wxDownloadFile * download = new wxDownloadFile(this, url, fileName, true);

                info->SetDownloader(download);
                m_Downloads[url] = info;

                download->Run();
                if(needAddNew)m_DownloadsListBox->AddNew();

                m_DownloadsListBox->RefreshLines(
                        m_DownloadsListBox->GetFirstVisibleLine(),
                        m_DownloadsListBox->GetLastVisibleLine());
        }
        while (false);
}

Собственно, что у нас здесь происходит:

  • отображается диалог создания новой загрузки. В нем необходимо ввести URL и путь к результирующему файлу
  • В случае если диалог отработал успешно, мы проверяем наличие загрузки с указанным URL в хеш-таблице
  • Если загрузка присутствует, то она останавливается
  • Если загрузки с указанным URL в таблице нет, то создается новый объект DownloadInfo
  • Затем создается объект wxDownloadFile и запускается
  • После этого происходит обновление списка загрузок

Как уже было сказано ранее, класс wxDownloadFile позволяет получать уведомления о состоянии загрузки. Для этого используется обработчик события EVT_DOWNLOAD():

BEGIN_EVENT_TABLE( MobileDownloaderMainFrame, wxFrame )
...
EVT_DOWNLOAD(MobileDownloaderMainFrame::OnDownloadStatus)
END_EVENT_TABLE()

void MobileDownloaderMainFrame::OnDownloadStatus(wxDownloadEvent & event)
{
        do
        {
                wxString url = event.GetDownLoadURL();
                DownloadInfo * downloadInfo = m_Downloads[url];
                wxDownloadFile * download = downloadInfo->GetDownloader();
                if(!download) break;
                downloadInfo->SetStatus((wxDownloadEvent::DownloadSTATUS)event.GetDownLoadStatus());
                switch(event.GetDownLoadStatus())
                {
                case wxDownloadEvent::DOWNLOAD_FAIL:
                        wxLogDebug(wxT("DOWNLOAD_FAIL"));
                        downloadInfo->SetDownloader(NULL);
                        break;
                case wxDownloadEvent::DOWNLOAD_COMPLETE:
                        wxLogDebug(wxT("DOWNLOAD_COMPLETE"));
                        downloadInfo->SetDownloader(NULL);
                        break;
                case wxDownloadEvent::DOWNLOAD_INPROGRESS:
                        wxLogDebug(wxT("DOWNLOAD_INPROGRESS"));
                        downloadInfo->SetDownloadedSize(event.GetDownLoadedBytesCount());
                        break;
                default:
                        break;
                }
                m_DownloadsListBox->RefreshLines(
                        m_DownloadsListBox->GetFirstVisibleLine(),
                        m_DownloadsListBox->GetLastVisibleLine());
        }
        while(false);
}

В обработчике события мы получаем состояние загрузки из объекта wxDownloadEvent и, в зависимости от этого значения, выполняем обновление объекта DownloadInfo в хеш-таблице загрузок.

О чем еще важно помнить? Приложение остается висеть в памяти пока не будут завершены все его потоки. wxDownloadFile создает поток на каждую загрузку и если мы попытаемся закрыть приложение во время скачивания файлов, то приложение будет выгружено только по завершению всех потоков. Чтобы такой ситуации у нас не было, нам необходимо принудительно завершить все потоки при закрытии главной формы приложения. Для этого мы будем использовать обработчик EVT_CLOSE():

void MobileDownloaderMainFrame::OnCloseWindow( wxCloseEvent& event )
{
        for(DownloadsHash::iterator i = m_Downloads.begin(); i != m_Downloads.end(); i++)
        {
                DownloadInfo * download = i->second;
                if(!download) continue;
                if(download->GetDownloader()) download->GetDownloader()->CancelDownload();
                wxDELETE(download);
                i->second = NULL;
        }
    event.Skip();
}

Вот теперь все.
wxMobileDownloader - Пример использования wxDownloadFile
Скачать исходный код приложения и проекты для Win32 и Windows Mobile.

Еще интересные посты о программировании для мобильных устройств:

No Comments

Make A Comment

No comments yet.

Comments RSS Feed   TrackBack URL

Leave a comment

Please leave these two fields as-is:
Поиск сотрудников или работы: психологи онлайн. ; Чай, кофе и шоколад: магазин чая. Китайские сорта чая оптом. ; Эксклюзивные Сувениры: сувенирная продукция с логотипом компании. ; Центр пластической хирургии: пластика носа цены. Пластическая хирургия. Скидки.

top