vendredi 16 décembre 2011

MoSync : Téléchargement de données depuis internet

Il y’a mille raisons pour lesquelles vous voudriez que votre application récupère des informations à partir d'Internet. Peut-être que vous voulez télécharger la bande dessinée Dilbert d’aujourd'hui, ou obtenir des données spécifiques pour l'utilisateur, ou de proposer des publicités, ou de se connecter à une interface web. Peut-être que vous souhaitez mettre en place un nouveau protocole, comme FTP ou Jabber. Dans ce tutoriel nous allons vous montrer comment obtenir des données par HTTP.

Modèles de connexion

MoSync fournit un large éventail d'objets et d’interfaces de connexion. Il y'a trois niveaux d'abstraction pour les connexions à Internet, et vous pouvez écrire du code de connexion par:
·         Utilisation de la méthode de l'API maConnect ()
·         Utilisation de la classe MAUtil::Connection
·         Utilisation des classes Downloader

Utilisation de la méthode de l’API maConnect()

Au niveau le plus bas, vous pouvez utiliser les fonctions de l'API MoSync directement. Vous pouvez utiliser la méthode maConnect () pour ouvrir une Socket pour se connecter à un port sur l'Internet. Vous pouvez implémenter des protocoles qui ne sont pas pris en charge dans MoSync à un niveau supérieur (comme HTTP et HTTPS) par le formatage d'une URL comme ceci:
socket://[:]
 de sorte que vous pouvez créer votre propre protocole pour le support d’IMAP et POP3 pour faire un client de messagerie par exemple.
Vous pouvez utiliser cette méthode pour vous connecter à des périphériques Bluetooth, avec le format :
btspp://

Nous allons nous concentrer sur les connexions HTTP dans ce tutoriel bien, et bien que l'API fournit des méthodes spécifiques de HTTP, nous allons nous concentrer sur les éléments de téléchargement fournis par MAUtil.

Utilisation de la classe MAUtil ::Connection

Le deuxième niveau d'abstraction est fournit par les objets MAUtil::Connection. Ce sont des objets C++ et sont beaucoup plus adaptés pour une utilisation dans une application Moblet.
MAUtil:: Connection vous permet un accès direct pour envoyer et recevoir des flux.
MAUtil:: HttpConnection construit connexions HTTP 1.0.
Avec les objets HttpConnection vous pouvez accéder au protocole HTTP 1.0 avec la possibilité de définir en-têtes HTTP avant d'envoyer la requête. Ils permettent également de créer des classes wrapper pour l’implémentation de nouvelles fonctions spécifiques HTTP. Par exemple, vous pouvez utiliser HttpConnection pour implémenter un système d'authentification en ligne, que vous pourrez ensuite réutiliser.
Les objets Connection envoient des messages aux ConnectionListeners. Lorsque vous travaillez avec Connection ou HttpConnection, vous aurez certainement besoin de créer une classe qui hérite de ConnectionListener ou HttpConnectionListener, et implémenter ses méthodes. Nous constatons que très souvent cette classe est un écran (Screen), que nous définissons comme:
class MyScreen : public Screen, ConnectionListener
Puis, on implémente les méthodes
void connectFinished(Connection* conn, int result);
void connRecvFinished(Connection* conn, int result);
void connWriteFinished(Connection* conn, int result);
void connReadFinished(Connection* conn, int result);
Ces méthodes de callback permettent d’implémenter le comportement de l’application par rapport aux événements de la connexion.

Utilisation de la classe Downloader

Au plus haut niveau se trouvent les classes Downloader. Ces classes enveloppent une grande partie de la mécanique obtention des données sur HTTP. Ce sont les classes que nous utilisons le plus souvent. Ce sont les classes qu’il faut si vous voulez juste de télécharger des XML ou un tableau par exemple.
La classe Downloader implémente elle-mêm HttpConnectionListener, et elle est très simple à utiliser. Il vous suffit d’indiquer l'URL duquel vous souhaitez obtenir des données ainsi que le MAHandle où charger les données.
Si vous voulez savoir quand votre téléchargement terminé, vous devez implémenter un Listener également. Pour ce faire, vous héritez et vous implémentez DownloadListener.
Un second objet de téléchargement est BuffDownloader. Il est un peu plus spécialisé, au lieu de télécharger à un MAHandler, il télécharge dans la mémoire Heap.
Il y’a deux autres objets de téléchargement à un niveau encore plus élevé: ImageDownloader et AudioDownloader. Ce sont deux implémentations de Downloader, ils créent les ressources appropriées dans la mémoire pour pourvoir les utiliser directement. Plus d'informations sur ces objets plus tard.
Enfin, vous pouvez créer vos propres Downloader. Vous pouvez hériter de Downloader, et effectuer certaines de vos propres tâches de téléchargement, tout comme vous pouvez implémenter Connection pour gérer votre propre protocole d'application.

Téléchargement de données depuis le Web

Selon toute probabilité, ce que vous voulez réellement faire c’est de télécharger des données à partir du Web. La meilleure façon de le faire est d'utiliser la classe Downloader et de créer un DownloadListener correspondant. Pour ce faire, nous pouvons écrire MyScreen qui demande le téléchargement de DownloadListener. Pour le faire d'une manière réutilisable, nous créons une classe DownloadScreen.

DownloadScreen.h

#ifndef _DOWNLOADSCREEN_H_
#define _DOWNLOADSCREEN_H_
#include
#include
#include
#include "IScreenCoordinator.h"
#include "..\Utilities/Util.h"
#include "..\Utilities\IDownloadScreenListener.h"
#include "..\Widgets\SoftKeyBar.h"
using namespace MAUI;
using namespace MAUtil;
class DownloadScreen : public Screen, public DownloadListener, public ISoftKeyBarListener
{
public:
    DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen);
    ~DownloadScreen();
    void download(const char* url, const char* storeName);
    void keyPressEvent(int keyCode);
    void setDownloadListener(IDownloadScreenListener* dll);
    void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
    void finishedDownloading(Downloader *dl, MAHandle data);
    void error(Downloader* dl, int code);
    void downloadCancelled(Downloader* dl);
    void softKeySelected(int buttonID);
private:
    Screen* previous;
    IScreenCoordinator* mainScreen;
    Layout* layout;
    Image* image;
    ListBox* listBox;
    Downloader* dl;
    IDownloadScreenListener* listener;
    MAHandle _store;
};
#endif //_DOWNLOADSCREEN_H_
Ce code va créer MyScreen, gérer les téléchargements, et informer son propre type de Listener (IDownloadScreenListener) lorsque les téléchargements sont terminés. (Il n'informe pas IDownloadScreenListener d'autre chose que la réussite, vu que l'interaction avec l'utilisateur pour un téléchargement échoué peut être traitée par cet écran.)

IDownloadListener.h

#ifndef _IDOWNLOADSCREENLISTENER_H_
#define _IDOWNLOADSCREENLISTENER_H_
//Interface
class IDownloadScreenListener
{
public:
    virtual void downloadComplete();
};
#endif //_IDOWNLOADSCREENLISTENER_H_
DownloadScreen.h est implémentée dans DownloadScreen.cpp:
#include  "DownloadScreen.h"
//Handles a download
const char* store;

DownloadScreen::DownloadScreen(Screen* previous, IScreenCoordinator* mainScreen)
 : previous(previous), mainScreen(mainScreen)
{
    dl = new Downloader();
    dl->addDownloadListener(this);

    image = (Image*)createMainLayout(BLANK, BACK_BUTTON, this);
    layout = (Layout*) image->getChildren()[0];
    listBox = (ListBox*) layout->getChildren()[1];
    Label* l = createLabel("Starting download", 32);
    listBox->add(l);
    this->setMain(image);
}

DownloadScreen::~DownloadScreen(void)
{
    if(dl->isDownloading())
    dl->cancelDownloading();    
    delete dl;
}

void DownloadScreen::setDownloadListener(IDownloadScreenListener* dll)
{
    listener = dll;
}

void DownloadScreen::download(const char *url, const char* storeName)
{
    if(dl->isDownloading())
    {
        //lprintfln("Busy.");
        //dl->cancelDownloading();
    }
    else
    {
        lprintfln("Downloading %s", url);
        store = storeName;
        dl->beginDownloading(url);
    }
}

void DownloadScreen::downloadCancelled(Downloader *dl)
{
    //lprintfln("Cancelled");
    listBox->add(createLabel("Download has been cancelled"));
}

void DownloadScreen::error(Downloader *dl, int code)
{
    lprintfln("Error: %d", code);
    listBox->add(createLabel("Sorry, an error has occured"));
}

void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
    //Save the store
    MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
    maWriteStore(h, data);
    maCloseStore(h, 0);

    lprintfln("Finished download");
    if(listener != NULL)
    {
        //lprintfln("Calling listener");
        listener->downloadComplete();
    }

    //lprintfln("Showing calling screen");
    previous->show();
}

void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
    Label* l = (Label*)listBox->getChildren()[0];
    char* cap = new char[255];
    sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
    l->setCaption(cap);
    delete[] cap;
    //lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}

void DownloadScreen::softKeySelected(int buttonID)
{
    switch(buttonID)
    {
    case SoftKeyBar::LSK:
        break;
    case SoftKeyBar::FIRE:
        break;
    case SoftKeyBar::RSK:
        previous->show();
        break;
    }
}

void DownloadScreen::keyPressEvent(int keyCode)
{
    switch(keyCode)
    {
    case MAK_2:
    case MAK_4:
    case MAK_UP:
        listBox->selectPreviousItem();
        break;
    case MAK_6:
    case MAK_8:
    case MAK_DOWN:
        listBox->selectNextItem();
        break;
    case MAK_0:
        if(dl->isDownloading())
        {
            dl->cancelDownloading();
        }
    }
}
Cet écran de téléchargement peut être utilisé depuis plusieurs autres écrans. Par exemple, si vous avez un écran qui affiche un calendrier où l'utilisateur peut naviguer sur différents jours pour voir les événements de chaque journée. Cet écran peut télécharger un paquet de données à partir d'Internet contenant les événements créés pour l'utilisateur. Lorsque vous voulez télécharger une mise à jour, myEventScreen appel DownloadScreen et lui transmet l'URL qu'il veut télécharger. Ce DownloadScreen s'occupe de toutes les opérations.
DownloadScreen contient un objet Downloader, et il implémente DownloadListener. Il crée un Downloader quand il est construit, et il le supprime lors de la destruction. Lorsque la méthode download de DownloadScreen est appelée, elle passe l'URL au Downloader et commence le téléchargement.
Comme le Downloader envoie des événements à DownloadScreen via l'interface DownloadListener, l'écran peut informer l'utilisateur sur leur téléchargement. Quand il a fini, il informe l'écran qu'il a terminé, de sorte que l'écran puisse continuer son exécution.
Pour l'utiliser, nous l’appelons à partir EventsScreen.cpp. Ce qui suit est une partie de son code :
#include "EventsScreen.h"
#include
EventsScreen::EventsScreen(Screen* previous, MainScreen* mainScreen)
: previous(previous), mainScreen(mainScreen)
{
            image =(Image*) createMainLayout(BLANK, BACK_BUTTON, this);
            layout = (Layout*) image->getChildren()[0];
            listBox = (ListBox*) layout->getChildren()[1];
            this->setMain(image);
            urlptr = NULL;
            dlscreen = new DownloadScreen(this, mainScreen);
            dlscreen->setDownloadListener(this);
}
EventsScreen::~EventsScreen()
{
            if(urlptr != NULL)
            delete [] urlptr;
            delete dlscreen;
}
void EventsScreen::downloadComplete()
{
            listBox->getChildren().clear();
            createEventScreen();
}
void EventsScreen::keyPressEvent(int keyCode)
{
            switch(keyCode)
            {
            case MAK_2:
            case MAK_4:
            case MAK_UP:
                        listBox->selectPreviousItem();
                        break;
            case MAK_6:
            case MAK_8:
            case MAK_DOWN:
                        listBox->selectNextItem();
                        break;
            case MAK_0:
                        dlscreen->download(formatUrl(), EVENTSTORAGE);
                        dlscreen->show();
                        break;
            }
}
Lorsque l'utilisateur appuie 0 sur son clavier, il envoie à DownloadScreen l'URL de téléchargement, et le nom du store pour y écrire les données qu’il a reçu.
Lorsque les données ont été complètement téléchargés, EventsScreen::DownloadComplete() est appelée, et l'écran peut se repeupler à partir des données téléchargées.

Réception des événements

Dans l’exemple ci-dessus, la classe DownloadScreen implémente DownloadListener. En particulier, elle implémente les fonctions :
void notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes);
void finishedDownloading(Downloader *dl, MAHandle data);
void error(Downloader* dl, int code);
void downloadCancelled(Downloader* dl);
En faisant cela, l’écran de téléchargement est informée à propos de ce qui arrive au downloader. Le code d’implémentation est comme ceci :
void DownloadScreen::downloadCancelled(Downloader *dl)
{
            //lprintfln("Cancelled");
            listBox->add(createLabel("Download has been cancelled"));
}
void DownloadScreen::error(Downloader *dl, int code)
{
            lprintfln("Error: %d", code);
            listBox->add(createLabel("Sorry, an error has occured"));
}
void DownloadScreen::finishedDownloading(Downloader *dl, MAHandle data)
{
            //Sauvegarder le store
            MAHandle h = maOpenStore(store, MAS_CREATE_IF_NECESSARY);
            maWriteStore(h, data);
            maCloseStore(h, 0);
            lprintfln("Finished download");
            if(listener != NULL)
            {
                        //lprintfln("Calling listener");
                        listener->downloadComplete();
            }
            //lprintfln("Showing calling screen");
            previous->show();
}
void DownloadScreen::notifyProgress(Downloader *dl, int downloadedBytes, int totalBytes)
{
            Label* l = (Label*)listBox->getChildren()[0];
            char* cap = new char[255];
            sprintf(cap, "Downloaded %d of %d bytes", downloadedBytes, totalBytes);
            l->setCaption(cap);
            delete[] cap;
            //lprintfln("Downloaded %d of %d bytes", downloadedBytes, totalBytes);
}
Quand le Downloader informe l’écran d’une erreur, ou que le téléchargement a été annulé, ou qu’il a téléchargé un peu de donnée, vous pourrez informer l’utilisateur à son tour. Quand le téléchargement termine, vous pouvez appeler IDownloadScreenListener, et afficher l’écran précédent.

Aucun commentaire: