Partager via


Partie 5 : Stratégies de partage de code pratiques

Cette section fournit des exemples de partage de code pour les scénarios d’application courants.

Couche de données

La couche de données se compose d’un moteur de stockage et de méthodes pour lire et écrire des informations. Pour les performances, la flexibilité et la compatibilité entre plateformes, le moteur de base de données SQLite est recommandé pour les applications multiplateformes Xamarin. Il s’exécute sur une grande variété de plateformes, notamment Windows, Android, iOS et Mac.

SQLite

SQLite est une implémentation de base de données open source. Vous trouverez la source et la documentation à SQLite.org. La prise en charge de SQLite est disponible sur chaque plateforme mobile :

  • iOS : intégré au système d’exploitation.
  • Android : intégré au système d’exploitation depuis Android 2.2 (niveau d’API 10).
  • Windows : consultez l’extension SQLite pour plateforme Windows universelle.

Même avec le moteur de base de données disponible sur toutes les plateformes, les méthodes natives permettant d’accéder à la base de données sont différentes. Les API intégrées iOS et Android permettent d’accéder à SQLite qui peuvent être utilisées à partir de Xamarin.iOS ou Xamarin.Android. Toutefois, l’utilisation des méthodes natives du KIT de développement logiciel (SDK) natives ne permet pas de partager du code (à part les requêtes SQL elles-mêmes, en supposant qu’elles sont stockées sous forme de chaînes). Pour plus d’informations sur la recherche CoreData de fonctionnalités de base de SQLiteOpenHelper données natives dans la classe iOS ou Android, car ces options ne sont pas multiplateformes, elles ne sont pas au-delà de l’étendue de ce document.

ADO.NET

Prise en charge System.Data de Xamarin.iOS et Xamarin.Android et Mono.Data.Sqlite (consultez la documentation Xamarin.iOS pour plus d’informations). L’utilisation de ces espaces de noms vous permet d’écrire ADO.NET code qui fonctionne sur les deux plateformes. Modifiez les références du projet pour inclure System.Data.dll et Mono.Data.Sqlite.dll ajouter ces instructions using à votre code :

using System.Data;
using Mono.Data.Sqlite;

Ensuite, l’exemple de code suivant fonctionne :

string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "items.db3");
bool exists = File.Exists (dbPath);
if (!exists)
    SqliteConnection.CreateFile (dbPath);
var connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
if (!exists) {
    // This is the first time the app has run and/or that we need the DB.
    // Copy a "template" DB from your assets, or programmatically create one like this:
    var commands = new[]{
        "CREATE TABLE [Items] (Key ntext, Value ntext);",
        "INSERT INTO [Items] ([Key], [Value]) VALUES ('sample', 'text')"
    };
    foreach (var command in commands) {
        using (var c = connection.CreateCommand ()) {
            c.CommandText = command;
            c.ExecuteNonQuery ();
        }
    }
}
// use `connection`... here, we'll just append the contents to a TextView
using (var contents = connection.CreateCommand ()) {
    contents.CommandText = "SELECT [Key], [Value] from [Items]";
    var r = contents.ExecuteReader ();
    while (r.Read ())
        Console.Write("\n\tKey={0}; Value={1}",
                r ["Key"].ToString (),
                r ["Value"].ToString ());
}
connection.Close ();

Les implémentations réelles de ADO.NET seraient évidemment divisées entre différentes méthodes et classes (cet exemple est destiné uniquement à des fins de démonstration).

SQLite-NET – ORM multiplateforme

Un Mappeur ORM (ou Object-Relational Mapper) tente de simplifier le stockage des données modélisées dans les classes. Au lieu d’écrire manuellement des requêtes SQL qui créent des TABLEs ou SELECT, INSERT et DELETE qui sont extraites manuellement des champs de classe et des propriétés, un ORM ajoute une couche de code qui le fait pour vous. À l’aide de la réflexion pour examiner la structure de vos classes, un ORM peut créer automatiquement des tables et des colonnes qui correspondent à une classe et générer des requêtes pour lire et écrire les données. Cela permet au code d’application d’envoyer et de récupérer simplement des instances d’objet à l’ORM, qui s’occupe de toutes les opérations SQL sous le capot.

SQLite-NET agit comme un ORM simple qui vous permettra d’enregistrer et de récupérer vos classes dans SQLite. Il masque la complexité de l’accès SQLite multiplateforme avec une combinaison de directives du compilateur et d’autres astuces.

Fonctionnalités de SQLite-NET :

  • Les tables sont définies en ajoutant des attributs aux classes Model.
  • Une instance de base de données est représentée par une sous-classe de SQLiteConnection , la classe principale dans la bibliothèque SQLite-Net.
  • Les données peuvent être insérées, interrogées et supprimées à l’aide d’objets. Aucune instruction SQL n’est requise (bien que vous puissiez écrire des instructions SQL si nécessaire).
  • Les requêtes Linq de base peuvent être effectuées sur les collections retournées par SQLite-NET.

Le code source et la documentation pour SQLite-NET sont disponibles sur SQLite-Net sur github et ont été implémentés dans les deux études de cas. Un exemple simple de code SQLite-NET (de l’étude de cas Tasky Pro ) est illustré ci-dessous.

Tout d’abord, la TodoItem classe utilise des attributs pour définir un champ comme clé primaire de base de données :

public class TodoItem : IBusinessEntity
{
    public TodoItem () {}
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Notes { get; set; }
    public bool Done { get; set; }
}

Cela permet de créer une TodoItem table avec la ligne de code suivante (et aucune instruction SQL) sur une SQLiteConnection instance :

CreateTable<TodoItem> ();

Les données de la table peuvent également être manipulées avec d’autres méthodes ( SQLiteConnection à nouveau, sans nécessiter d’instructions SQL) :

Insert (TodoItem); // 'task' is an instance with data populated in its properties
Update (TodoItem); // Primary Key field must be populated for Update to work
Table<TodoItem>.ToList(); // returns all rows in a collection

Consultez le code source de l’étude de cas pour obtenir des exemples complets.

Accès aux fichiers

L’accès aux fichiers est certain d’être une partie clé de n’importe quelle application. Voici quelques exemples courants de fichiers qui peuvent faire partie d’une application :

  • Fichiers de base de données SQLite.
  • Données générées par l’utilisateur (texte, images, son, vidéo).
  • Données téléchargées pour la mise en cache (images, fichiers html ou PDF).

accès direct System.IO

Xamarin.iOS et Xamarin.Android autorisent l’accès au système de fichiers à l’aide de classes dans l’espace System.IO de noms.

Chaque plateforme a des restrictions d’accès différentes qui doivent être prises en compte :

  • Les applications iOS s’exécutent dans un bac à sable avec un accès très restreint au système de fichiers. Apple détermine également comment utiliser le système de fichiers en spécifiant certains emplacements sauvegardés (et d’autres non). Pour plus d’informations, consultez le guide Utilisation du système de fichiers dans Xamarin.iOS .
  • Android limite également l’accès à certains répertoires liés à l’application, mais il prend également en charge les médias externes (par exemple. Cartes SD) et accès aux données partagées.
  • Windows Phone 8 (Silverlight) n’autorise pas l’accès direct aux fichiers : les fichiers ne peuvent être manipulés qu’à l’aide IsolatedStoragede .
  • Les projets Windows 8.1 WinRT et Windows 10 UWP offrent uniquement des opérations de fichier asynchrones via Windows.Storage des API, qui sont différentes des autres plateformes.

Exemple pour iOS et Android

Un exemple trivial qui écrit et lit un fichier texte est illustré ci-dessous. L’utilisation Environment.GetFolderPath permet au même code de s’exécuter sur iOS et Android, qui retournent chacun un répertoire valide en fonction de leurs conventions de système de fichiers.

string filePath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "MyFile.txt");
System.IO.File.WriteAllText (filePath, "Contents of text file");
Console.WriteLine (System.IO.File.ReadAllText (filePath));

Pour plus d’informations sur les fonctionnalités de système de fichiers spécifiques à iOS, consultez le document Xamarin.iOS Working with the File System . Lorsque vous écrivez du code d’accès aux fichiers multiplateforme, n’oubliez pas que certains systèmes de fichiers respectent la casse et ont différents séparateurs de répertoires. Il est recommandé d’utiliser toujours la même casse pour les noms de fichiers et la méthode lors de la Path.Combine() construction de chemins de fichier ou de répertoire.

Windows.Storage pour Windows 8 et Windows 10

The Creating Mobile Apps with Xamarin.Forms bookChapter 20. Les E/S asynchrones et de fichiers incluent des exemples pour Windows 8.1 et Windows 10.

L’utilisation d’un DependencyService fichier peut être lu et fichier sur ces plateformes à l’aide des API prises en charge :

StorageFolder localFolder = ApplicationData.Current.LocalFolder;
IStorageFile storageFile = await localFolder.CreateFileAsync("MyFile.txt",
                                        CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(storageFile, "Contents of text file");

Pour plus d’informations, reportez-vous au chapitre 20 du livre.

Stockage isolé sur Windows Phone 7 &8 (Silverlight)

Le stockage isolé est une API courante pour enregistrer et charger des fichiers sur toutes les plateformes iOS, Android et les anciennes plateformes Windows Phone.

Il s’agit du mécanisme par défaut pour l’accès aux fichiers dans Windows Phone (Silverlight) qui a été implémenté dans Xamarin.iOS et Xamarin.Android pour permettre l’écriture du code d’accès aux fichiers commun. La System.IO.IsolatedStorage classe peut être référencée sur les trois plateformes d’un projet partagé.

Pour plus d’informations, consultez la vue d’ensemble du stockage isolé pour Windows Phone .

Les API de stockage isolé ne sont pas disponibles dans les bibliothèques de classes portables. Une alternative pour PCL est le NuGet PCLStorage

Accès aux fichiers multiplateformes dans les listes de contrôle d’accès

Il existe également un NuGet compatible PCL – PCLStorage – qui permet d’accéder aux fichiers multiplateformes pour les plateformes prises en charge par Xamarin et les dernières API Windows.

Opérations réseau

La plupart des applications mobiles auront un composant réseau, par exemple :

  • Téléchargement d’images, de vidéos et d’audio (par exemple, miniatures, photos, musique).
  • Téléchargement de documents (par exemple, HTML, PDF).
  • Chargement de données utilisateur (telles que des photos ou du texte).
  • Accès aux services web ou API tierces (y compris SOAP, XML ou JSON).

Le .NET Framework fournit quelques classes différentes pour accéder aux ressources réseau : HttpClient, WebClientet HttpWebRequest.

HttpClient

La HttpClient classe de l’espace System.Net.Http de noms est disponible dans Xamarin.iOS, Xamarin.Android et la plupart des plateformes Windows. Il existe une bibliothèque cliente Microsoft HTTP NuGet qui peut être utilisée pour intégrer cette API dans des bibliothèques de classes portables (et Windows Phone 8 Silverlight).

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://xamarin.com");
var response = await myClient.SendAsync(request);

WebClient

La WebClient classe fournit une API simple pour récupérer des données distantes à partir de serveurs distants.

plateforme Windows universelle opérations doivent être asynchrones, même si Xamarin.iOS et Xamarin.Android prennent en charge les opérations synchrones (qui peuvent être effectuées sur des threads d’arrière-plan).

Le code d’une opération asychronous WebClient simple est le suivant :

var webClient = new WebClient ();
webClient.DownloadStringCompleted += (sender, e) =>
{
    var resultString = e.Result;
    // do something with downloaded string, do UI interaction on main thread
};
webClient.Encoding = System.Text.Encoding.UTF8;
webClient.DownloadStringAsync (new Uri ("http://some-server.com/file.xml"));

WebClient a DownloadFileCompleted également et DownloadFileAsync pour récupérer des données binaires.

HttpWebRequest

HttpWebRequest offre plus de personnalisation que WebClient et, par conséquent, nécessite plus de code à utiliser.

Le code d’une opération synchrone HttpWebRequest simple est le suivant :

var request = HttpWebRequest.Create(@"http://some-server.com/file.xml ");
request.ContentType = "text/xml";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
    if (response.StatusCode != HttpStatusCode.OK)
        Console.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        var content = reader.ReadToEnd();
        // do something with downloaded string, do UI interaction on main thread
    }
}

Il existe un exemple dans notre documentation sur les services web.

Reachability

Les appareils mobiles fonctionnent sous une variété de conditions réseau entre les connexions Wi-Fi rapides ou 4G aux zones de réception médiocres et les liaisons de données EDGE lentes. En raison de cela, il est recommandé de détecter si le réseau est disponible et, le cas échéant, quel type de réseau est disponible, avant de tenter de se connecter à des serveurs distants.

Les actions qu’une application mobile peut prendre dans ces situations sont les suivantes :

  • Si le réseau n’est pas disponible, conseillez l’utilisateur. S’ils l’ont désactivé manuellement (par exemple, Mode avion ou désactivation du Wi-Fi), ils peuvent résoudre le problème.
  • Si la connexion est 3G, les applications peuvent se comporter différemment (par exemple, Apple ne permet pas aux applications de plus de 20 Mo d’être téléchargées sur 3G). Les applications peuvent utiliser ces informations pour avertir l’utilisateur des temps de téléchargement excessifs lors de la récupération de fichiers volumineux.
  • Même si le réseau est disponible, il est recommandé de vérifier la connectivité avec le serveur cible avant de lancer d’autres requêtes. Cela empêche les opérations réseau de l’application de expirer à plusieurs reprises et d’autoriser l’affichage d’un message d’erreur plus informatif à l’utilisateur.

WebServices

Consultez notre documentation sur l’utilisation des services web, qui couvre l’accès aux points de terminaison REST, SOAP et WCF à l’aide de Xamarin.iOS. Il est possible d’élaborer manuellement des demandes de service web et d’analyser les réponses, mais il existe des bibliothèques disponibles pour simplifier ce processus, notamment Azure, RestSharp et ServiceStack. Même les opérations WCF de base sont accessibles dans les applications Xamarin.

Azure

Microsoft Azure est une plateforme cloud qui fournit un large éventail de services pour les applications mobiles, notamment le stockage et la synchronisation des données, et les notifications Push.

Visitez azure.microsoft.com pour essayer gratuitement.

RestSharp

RestSharp est une bibliothèque .NET qui peut être incluse dans les applications mobiles pour fournir un client REST qui simplifie l’accès aux services web. Il permet de fournir une API simple pour demander des données et analyser la réponse REST. RestSharp peut être utile

Le site web RestSharp contient de la documentation sur l’implémentation d’un client REST à l’aide de RestSharp. RestSharp fournit des exemples Xamarin.iOS et Xamarin.Android sur github.

Il existe également un extrait de code Xamarin.iOS dans notre documentation des services web.

ServiceStack

Contrairement à RestSharp, ServiceStack est une solution côté serveur pour héberger un service web ainsi qu’une bibliothèque cliente qui peut être implémentée dans les applications mobiles pour accéder à ces services.

Le site web ServiceStack explique l’objectif du projet et des liens vers des exemples de documents et de code. Les exemples incluent une implémentation complète côté serveur d’un service web ainsi que diverses applications côté client qui peuvent y accéder.

WCF

Les outils Xamarin peuvent vous aider à utiliser certains services Windows Communication Foundation (WCF). En général, Xamarin prend en charge le même sous-ensemble côté client de WCF fourni avec le runtime Silverlight. Cela inclut les implémentations de protocole et d’encodage les plus courantes de WCF : messages SOAP codés en texte sur le protocole de transport HTTP à l’aide du BasicHttpBindingprotocole .

En raison de la taille et de la complexité de l’infrastructure WCF, il peut y avoir des implémentations de service actuelles et futures qui se situent en dehors de l’étendue prise en charge par le domaine de sous-ensemble client de Xamarin. En outre, la prise en charge de WCF nécessite l’utilisation d’outils uniquement disponibles dans un environnement Windows pour générer le proxy.

Thread

La réactivité des applications mobiles est importante : les utilisateurs s’attendent à ce que les applications se chargent et s’exécutent rapidement. Un écran « figé » qui cesse d’accepter l’entrée utilisateur semble indiquer que l’application s’est bloquée. Il est donc important de ne pas lier le thread d’interface utilisateur à des appels bloquants de longue durée, tels que les demandes réseau ou les opérations locales lentes (telles que la décompression d’un fichier). En particulier, le processus de démarrage ne doit pas contenir de tâches longues : toutes les plateformes mobiles tuent une application qui prend trop de temps à charger.

Cela signifie que votre interface utilisateur doit implémenter un « indicateur de progression » ou une interface utilisateur « utilisable » rapide à afficher et des tâches asynchrones pour effectuer des opérations en arrière-plan. L’exécution de tâches en arrière-plan nécessite l’utilisation de threads, ce qui signifie que les tâches en arrière-plan ont besoin d’un moyen de communiquer avec le thread principal pour indiquer la progression ou le moment où elles sont terminées.

Bibliothèque de tâches parallèles

Les tâches créées avec la bibliothèque de tâches parallèles peuvent s’exécuter de manière asynchrone et retourner sur leur thread appelant, ce qui les rend très utiles pour déclencher des opérations de longue durée sans bloquer l’interface utilisateur.

Une opération de tâche parallèle simple peut ressembler à ceci :

using System.Threading.Tasks;
void MainThreadMethod ()
{
    Task.Factory.StartNew (() => wc.DownloadString ("http://...")).ContinueWith (
        t => label.Text = t.Result, TaskScheduler.FromCurrentSynchronizationContext()
    );
}

La clé consiste TaskScheduler.FromCurrentSynchronizationContext() à réutiliser le thread SynchronizationContext.Current du thread appelant la méthode (ici le thread principal en cours d’exécution MainThreadMethod) comme moyen de marshaler les appels à ce thread. Cela signifie que si la méthode est appelée sur le thread d’interface utilisateur, elle réexécute l’opération ContinueWith sur le thread d’interface utilisateur.

Si le code démarre des tâches à partir d’autres threads, utilisez le modèle suivant pour créer une référence au thread d’interface utilisateur et la tâche peut toujours la rappeler :

static Context uiContext = TaskScheduler.FromCurrentSynchronizationContext();

Appel sur le thread d’interface utilisateur

Pour le code qui n’utilise pas la bibliothèque de tâches parallèles, chaque plateforme a sa propre syntaxe pour le marshaling des opérations vers le thread d’interface utilisateur :

  • iOS : owner.BeginInvokeOnMainThread(new NSAction(action))
  • Androidowner.RunOnUiThread(action)
  • Xamarin.FormsDevice.BeginInvokeOnMainThread(action)
  • Windows : Deployment.Current.Dispatcher.BeginInvoke(action)

La syntaxe iOS et Android nécessite une classe « context » qui signifie que le code doit passer cet objet à toutes les méthodes qui nécessitent un rappel sur le thread d’interface utilisateur.

Pour effectuer des appels de thread d’interface utilisateur dans du code partagé, suivez l’exemple IDispatchOnUIThread (avec la permission de @follesoe). Déclarez et programmez une IDispatchOnUIThread interface dans le code partagé, puis implémentez les classes spécifiques à la plateforme, comme indiqué ici :

// program to the interface in shared code
public interface IDispatchOnUIThread {
    void Invoke (Action action);
}
// iOS
public class DispatchAdapter : IDispatchOnUIThread {
    public readonly NSObject owner;
    public DispatchAdapter (NSObject owner) {
        this.owner = owner;
    }
    public void Invoke (Action action) {
        owner.BeginInvokeOnMainThread(new NSAction(action));
    }
}
// Android
public class DispatchAdapter : IDispatchOnUIThread {
    public readonly Activity owner;
    public DispatchAdapter (Activity owner) {
        this.owner = owner;
    }
    public void Invoke (Action action) {
        owner.RunOnUiThread(action);
    }
}
// WP7
public class DispatchAdapter : IDispatchOnUIThread {
    public void Invoke (Action action) {
        Deployment.Current.Dispatcher.BeginInvoke(action);
    }
}

Les développeurs Xamarin.Forms doivent utiliser Device.BeginInvokeOnMainThread dans du code commun (projets partagés ou PCL).

Fonctionnalités de plateforme et d’appareil et dégradation

D’autres exemples spécifiques de gestion des différentes fonctionnalités sont fournis dans la documentation fonctionnalités de la plateforme. Il traite de la détection de différentes fonctionnalités et de la façon de dégrader correctement une application pour fournir une bonne expérience utilisateur, même quand l’application ne peut pas fonctionner à son plein potentiel.