vendredi 16 décembre 2011

MoSync : Ecriture et lecture des données

Il y’a plusieurs méthodes pour écrire et lire des données depuis un appareil mobile. Lire et écrire des données internet peut être fait avec la classe Connection, mais ca reste difficile. La façon la plus simple d’écrire et lire des données, c’est avec le store MoSync.

Création des stores

Les stores sont enregistrés dans l’appareil mobile, et sont supportés par l’ensemble des mobiles compatibles MoSync.
Pour créer un store utilisez la fonction maOpenStore(). Cette fonction prend  paramètres, un const char* avec le nom du store, et un int représentant les options du store. Pour le moment, la seule option qui existe est MAS_CREATE_IF_NECESSARY et elle créera un store s’il n’existait pas au préalable.
La fonction maOpenStore() retourne un MAHandle, une référence au store que vous pouvez utiliser avec d’autres fonctions.
MAHandle myStore = maOpenStore("MyStore", MAS_CREATE_IF_NECESSARY);
Si le store n’existe pas dans le mobile, il sera créé et le MAHandle sera retourné. S’il existe déjà, le handle sera simplement retourné.
Si le fichier ne peut être créé (par exemple, il n’y a plus d’espace disponible ou une erreur du système) la valeur retournée sera l’une des valeurs de STERR.
Quand vous lancez votre application dans l’émulateur MoRE, vous remarquerez que les stores sont écrits dans un répertoire /stores dans votre répertoire output. Les stores ne peuvent pas être déployés avec votre application mais seulement créés à l’exécution.

Vérifier qu’un store existe

Si vous voulez vérifier si un store existe ou pas, appelez maOpenStore() avec les options à 0, la valeur STERR_NONEXISTENT sera retournées si le store n’existe pas.
MAHandle testStore = maOpenStore("MyStore", 0);
       
if(myStore == STERR_NONEXISTENT)
{
      // Store n’existe pas
}
else
{
       // Store existe avec comme handle testStore
}

Ecrire dans un store

Les stores n’ont pas un système sophistiqué d’écriture/lecture. Ils fournissent simplement une méthode pour écrire des données dans le mobile. A chaque fois que le store est écrit, il remplace le précédent. Vous ne pouvez pas ajouter ou modifier des données d’un store existent. Le store est écrit depuis une ressource de données. Cette ressource n’est pas redimensionnable, mais vous pouvez éditer son contenu, écrire et lire des variables dessus.
Vous pouvez créer des ressources de données à l’exécution, mais vous devez définir un MAHandle d’abord. Pour créer une ressource de données, utilisez la fonction maCreateData(). Elle retourne RES_OK s’il y’a assez de mémoire ou RES_OUT_OF_MEMORY sinon. Vous devez passer le MAHandle comme paramètre, en plus de la taille du store. Vous pouvez créer un nouveau MAHandle vide avec la fonction maCreatePlaceHolder().
String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{

}
Vous pouvez écrire des données en utilisant la fonction maWriteData()
String password = "p45sw0rd";
MAHandle myData = maCreatePlaceholder();
if(maCreateData(myData, password.length()) == RES_OK)
{
    maWriteData(myData, password.c_str(), 0, password.length();
}
La fonction maWriteData() nécessite le MAHandle de la ressource de données, un pointeur vers l’objet que vous souhaitez écrire, le décallage depuis le début de la ressource de données et la taille des données que vous souhaitez écrire. Ce qui veut dire que vous pouvez écrire la variable là où vous voulez dans la ressource de données. Si vous écrivez plusieurs données (variables), vous devez ou bien garder trace de la position relative d’écriture, ou utiliser un DataHandler qui va le faire pour vous.
Pour écrire la ressource de données dans le store, vous devez utiliser la fonction maWriteStore(). Elle prend le MAHandle du store, et le MAHandle des données.
int result = maWriteStore(myStore, myData);
Tous ce qui était avant dans le store sera remplacé. Si maWriteStore() retourne une valeur >0, cela veut dire que les données ont été correctement écrits. Les autres valeurs devraient être mappées à STERR. Ce qu’il faut vérifier ici c’est STERR_FULL qui veut dire qu’il n’y a plus d’espace de stockage.
int result = maWriteStore(myStore, myData);

switch(result)
{
    case > 0:
        //Tout va bien, les données sont sauvegardées
        break;
    case STERR_FULL:
        //Echec, pas assez d’espace pour sauvegarder
        break;
    case STERR_NONEXISTENT:
        //Echec, le store n’existe pas
        break;
    case STERR_GENERIC:
        //erreur inconnue, probablement une faute du mobile
        break;
}

Une note sur la sécurité des stores

Même si dans l’exemple, on vous montre un mot de passe qui s’écrit dans le store, vous devez être conscients que ces données ne sont pas complètement privées. Différents systèmes offrent différentes mesures de sécurité. Dans un mobile J2ME, ces données sont pratiquement sécurisées. Un hacker devra savoir beaucoup de choses sur les données pour pouvoir y accéder. Dans les Symbian S60 par contre, l’utilisateur peut librement naviguer dans les stores en utilisant le FileManager. Des stores en format texte brut peuvent être ouverts et lus, et les images écrits dans les stores peuvent être affichées. Si les données que vous souhaitez écrire sont sensibles, il vaut mieux les crypter avant.

Fermer un store

Quand vous aurez fini d’écrire, vous devez fermer le store. Cela peut être fait avec la fonction maCloseStore().
maCloseStore(myStore, 0);
Le second paramètre (0 dans l’exemple), indique si le store doit être supprimé ou pas à la fermeture. Si c’est 0, vous voulez le garder, sinon, il sera détruit. C’est le seul moyen de supprimer des stores.
void deleteStore(const char* storeName)
{
    MAHandle store = maOpenStore(storeName, 0);
    if(store != STERR_NONEXISTENT)
    {
        maCloseStore(store, 1); //Supprimer le store
    }
}

Lire depuis un store

Tout comme vous écrivez dans les stores depuis des ressources de données, vous les lisez de la même façon. La fonction maReadStore() copie les données d’un store vers le une ressource de données, indiqué par un MAHandle.
MAHandle myData = maCreatePlaceholder();
MAHandle myStore = maOpenStore("MyStore", 0);
if(myStore != MAS_NONEXISTENT)
{
    //Le store existe, nous pouvons lire depuis
    int result = maReadStore(myStore, myData);
    if(result == RES_OUT_OF_MEMORY)
    {
        //Ce store est trop grand pour le lire dans la mémoire
    }
}
Une fois le store lu dans la ressource de données, les valeurs peuvent y être lues. Bien sûr, vous devez connaitre combien de données vous allez lire : vous pouvez récupérer la taille des données avec la fonction maGetDataSize(), puis lire les données en utilisant la fonction maReadData().
char password[maGetDataSize(myData)];
maReadData(myData, &password, 0, maGetDataSize(myData));
Pour utiliser maReadData(), vous devez savoir combien d’octets il faut lire depuis la ressource de données. Dans l’exemple précédent, le mot de passe est la seule donnée stockée, alors on peut facilement lire toutes les données.
Si le store contient un login et un mot de passe, ca devient plus compliqué. Vous n’utilisez probablement pas des tailles fixes pour les String, alors vous devez enregistrer des données complémentaires sur la taille de chaque String. Si vous insérez un octet contenant la taille du String (un int s’il est trop long) juste avant les données du String, vous pourrez le lire très facilement. C’est ce qu’on appelle des String Pascal ou P-String.
//Lire un p-string
byte len = 0;

//Vous devez garder la trace de votre position
int position = 0;

//Lire la taille
maReadData(myData, &len, position, 1);// Lire un octet depuis position

//Passer à l’octet suivant
position++;

//Lire le string
char password[len + 1];  //Le String peut ne pas se terminer avec un Null
maReadData(myData, &password, position, len);

//Ajouter un null à la fin
password[len+1] = '/0';

//Déplacer la position
position += len;
Si vous êtes entrain de lire plusieurs variables, vous devez garder l’indexe de votre position dans les données.
Une fois que vous aurez fini avec les données, vous pouvez les libérer de la mémoire.
maDestroyObject(myData);
Ceci supprimer la ressource de données de la mémoire, mais ne détruit pas le store.

Utilisation du DataHandler

Si vous voulez écrire beaucoup de variables dans la ressource de données, puis dans le store, vous pouvez utiliser un utilitaire nommé DataHandler. Ca aide à écrire plusieurs variables dans la ressource en gardant trace des décalages pour vous. Vous devez inclure le header MAUtil/DataHandler.h
#include "MAUtil/DataHandler.h"
using namespace MAUtil;

...

String username = "m0sync";
String password = "p45sw0rd";

//Sauvegarder les valeurs dans la ressource de données
MAHandle myData = maCreatePlaceholder();
int size = username.length() + 4 + password.length() + 4; //La taille des données dont nous aurons besoin, incluant 4 octets pour la longueur de chaque String

if(maCreateData(myData, size) == RES_OK)
{
    DataHandler* handler = new DataHandler(myData);

    int usernameLength =  username.length();
    handler->write(&usernameLength, 4);
    handler->write(username.c_str(), usernameLength);

    int passwordLength = password.length();
    handler->write(&passwordLength, 4);
    handler->write(password.c_str(), passwordLength);
}

int result = maWriteStore(myStore, myData);

if(result > 0)
{
    //Ecrit avec succès
    maCloseStore(myStore, 0);
}
else
{
    //Echoué, supprimer le store
    maCloseStore(myStore, 1);
}
Comme vous pouvez le voir dans l’exemple ci-dessus, la classe DataHandler enlève la complexité de lire et écrire dans les stores, en les traitant comme les classes Java StreamReader et StreamWriter. Contrairement à l’exemple ci-dessus où nous lisons la ressource en gardant trace de l’indexe de la position, le DataHandler le gérera pour nous, et permettra un accès en série.

Aucun commentaire: