Pierre's Embedded and Mobile Blog

Soulevons le capot des systèmes embarqués et mobiles

May, 2012

Posts
  • Pierre's Embedded and Mobile Blog

    Stratégies et techniques de partage de code C# et XAML entre Windows Phone et Windows 8 Release Preview : Partie 1 : Introduction – les différences entre les plateformes à avoir en tête

    • 0 Comments

    Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

    http://sdrv.ms/K4BXdN

     

    Les différences entre les plateformes Windows Phone et Windows 8 Release Preview à avoir en tête au moment du portage.

    En tant que développeur vous avez probablement une vue orientée “fonctionnalités” de votre application. C’est une vue cohérente qu’il est important d’avoir mais pour que l’opération de portage soit vraiment bénéfique à l’utilisateur, il faut se mettre à sa place, et penser que l’usage de votre application sera probablement différent d’une plateforme à une autre.

    Un aspect particulièrement important est le facteur de forme : il s’agit de profiter des 10 pouces en mode paysage que propose un écran de tablette par rapport aux 4 pouces en mode portrait d’un écran de téléphone par exemple ! Une ergonomie pertinente sur un type d’écran ne le sera pas forcément sur un autre, particulièrement si on considère la manière d’interagir avec ce terminal : avec un ou plusieurs doigts, ou avec une souris dans le cadre d’un PC.

    Toujours sur le même sujet, il faut savoir que les applications « Metro » tournant sur la Windows 8 Release Preview peuvent fonctionner en plein écran, en mode « snapped » ou en mode « filled » c’est-à-dire en partageant l’écran avec une autre application. Pour chacun de ces modes les proportions de l’écran sont différentes, et l’usage que l’utilisateur en fera aussi (puisqu’il fera 2 choses à la foi) !

    Un autre exemple de point important à garder en tête est la présence (ou l’absence) de certains capteurs comme le GPS, ou bien les capteurs d’accélération ou de position.

    Coté logiciel, il y a aussi des différences significatives, pas forcément dans les fonctionnalités, mais plutôt dans la manière de les utiliser. Par exemple les paramètres, la recherche ou le partage unifiés entre les applications dans Windows 8 Release Preview ne fonctionnent pas de la même manière que Windows Phone.

    En conséquence de quoi, avant d’aborder la partie technique du portage de votre application, je vous encourage à repenser l’usage en fonction du facteur de forme… Il y a de nombreux articles détaillant l’expérience utilisateurs de Windows 8 Release Preview ou de Windows Phone qui pourront vous guider dans votre réflexion:

    · Pour Windows Phone : User Experience Design Guidelines for Windows Phone

    · Pour Windows 8 Release Preview : Windows Dev Center: Designing UX for Apps

    >> Vers la partie 2: Le processus de migration/portage du code

  • Pierre's Embedded and Mobile Blog

    Stratégies et techniques de partage de code C# et XAML entre Windows Phone et Windows 8 Release Preview : Partie 2: Le processus de migration/portage du code

    • 0 Comments

    Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

    http://sdrv.ms/K4BXdN

    Le Processus de Portage.

    Avant toute chose: Travaillez votre expérience utilisateur.

    Comme dit dans la partie précédente : avant de plonger dans le code, travaillez votre expérience utilisateur : que ce soit simplement avec un papier et un crayon, ou bien avec Sketchflow, ou PowerPoint, ou n’importe quel autre outil de mockup. Ce travail vous fournira une ligne directrice pendant tout le reste du travail de portage.

     

    Étape 1 : Savoir regarder son code, froidement

    [Dette technique] http://en.wikipedia.org/wiki/Technical_debt

    L’idée de dette technique est la suivante : le plus vous « cassez » votre code en y appliquant des corrections rapides et pas forcément très propres, le plus vous aurez de problèmes à résoudre pour faire évoluer votre code. L’ensemble de ces problèmes représente votre dette technique.

    La première étape quand vous portez votre application sur une nouvelle plateforme, c’est de revoir votre code. Cette revue de code est souvent prise un peu trop à la légère au profit de résultats rapide, et aussi parce qu’en tant que développeur on a tendance à croire qu’on connait son code par cœur. En fait, durant tout le développement et particulièrement durant le débuggage et la phase de tests pré-certification, nous cassons tous les patterns pour résoudre des « petits bugs », tout en accroissant notre dette technique. Tous ces petits fix ne sont pas forcément très lisibles, ni frais dans notre mémoire. Enfin, même si vous avez déjà revu votre code dans le passé, le revoir une nouvelle fois avec l’idée du portage en tête vous en donnera surement une lecture différente.

     

    Le but de cette revue de code est donc de rafraichir votre mémoire sur les différents composants de votre application, les couches de votre architecture, et à quel point ils sont dépendants les uns des autres. A la fin de cette revue de code, vous aurez probablement en tête un plan de refactoring du code pour préparer le portage... Une étape que vous auriez surement négligée si vous n’aviez pas fait cette revue !

     

    A ce point du processus, il est facile de céder à la tentation de copier vos fichiers sources dans un nouveau projet d’application Metro, et de commenter tout ce qui ne compile pas, pour avoir un début de prototype en 10 minutes. C’est une approche pragmatique mais qui risque d’être pénalisante pour votre utilisateur, car elle vous pousse à conserver une expérience existante parfois inadaptée, et vous risquez d’augmenter votre dette technique alors que le but de la revue de code précédente était justement de la diminuer !

    Que faut-il chercher pendant cette revue de code “spéciale portabilité”?

    Pour chaque projet de votre solution, vous devez lister les références que vous utilisez et à quel point elles sont portables. Si vous utilisez des frameworks tiers, il faut savoir s’il en existe des versions portables (soit à travers la portable library, ou ciblant spécifiquement chaque plateforme). Le résultat de cette investigation sera surement que cela existe, étant donné que c’est le but même de l’existence de ces frameworks… Mais parfois vous constaterez que les fonctionnalités ne sont pas les mêmes d’une version à une autre, ou bien qu’elles ne sont tout simplement pas au même niveau de maturité ou de stabilité. A vous de voir dans ce cas, si vous souhaitez vous attaquer plutôt au portage de l’existent ou tenter de remplacer par un framework concurrent. En tout état de cause, c’est plus facile si le code source existe, et dans ce cas-là, pourquoi ne pas contribuer à la communauté ?

     

    En ce qui me concerne, j’utilise surtout Json.Net et parfois MVVM Light, qui sont tous deux très portables.

     

    Pour chacun des fichiers sources, essayez de nettoyer vos “using” par exemple en utilisant la fonctionnalité de Visual Studio qui permet de les réorganiser. Avoir la liste la plus courte possible de « using » permet de comprendre plus facilement les dépendances de vos fichiers et de vos références.

     

    image

     

    Pour chaque appel à une fonctionnalité ou une API spécifique de la plateforme, voyez si l’équivalent existe sur l’autre plateforme. Il y a quelques billets dans la communauté qui relatent l’expérience des développeurs sur le sujet :

     

     

    Le dernier lien pointe vers une série de billets de mon collègue Jared Bienz qui promet d’être particulièrement bien détaillée.

     

    Si vous disposez d’une version de Visual Studio qui embarque les outils d’architecture (Premium ou Ultimate), c’est peut-être aussi l’occasion de s’en servir : ils sont capables de générer des graphes qui permettent de voir tout de suite à quel points vous avez cassé les patterns que vous avez utilisé, et les dépendances entre vos composants.

     

    image

     

    Voici un exemple de ce que j’obtiens avec une application cross-platform en cours d’écriture :

     

    image

     

    On voit à la fois dans l’application elle-même (FoursquareTracker) le pattern MVVM et la séparation des namespaces (qui correspond à peu près aux assemblies) et les dépendances à des namespaces externes.

     

    Cette étape vous permettra d’avoir une idée à des dépendances de vos fichiers entre eux, et vous mènera naturellement à l’étape 2, qui consiste à ré-architecturer le code en modules portables individuellement. Une fois que ces modules seront découpés, il ne restera plus qu’à choisir la bonne stratégie de portage pour chacun.

     

    Etape 2 : Séparer les composants (ou modules) du code et choisir la bonne technique de portage pour chacun

    Pour le reste de l’article nous allons considérer qu’un composant (ou module) est un bout de code faiblement lié au reste de l’application. Si vous n’avez pas encore réussi à isoler vos composants (ce que j’appelle du code « spaghetti »), il faut revenir à l’étape de revue de code et de refactoring. En tout état de cause, démarrer un portage sur du code spaghetti c’est s’assurer à terme de perdre du temps et de passer un assez mauvais moment à fabriquer quelque chose de bancal. C’est donc le dernier moment pour faire votre nettoyage de printemps, et ça commence avec le choix de bons design patterns…

     

    Un design pattern est une solution prouvée à un problème récurrent. C’est le résultat de travail de développeurs qui ont essuyés les plâtres avant vous. La plupart des designs patterns ont un objectif principal qui est la « séparation des problèmes » (separation of concerns). En d’autres mots, faire en sorte que le code soit découpé en unités fonctionnelles faiblement couplées pouvant évoluer indépendamment les unes des autres. Utiliser un design pattern permet souvent d’améliorer la testabilité du code, sa lisibilité, et même, sa portabilité ! Il faut toutefois faire attention à ne pas surutiliser les patterns, un défaut classique de développeur chevronné qui oublie que si un jour ce code est repris par quelqu’un de moins sénior, il sera surement moins lisible car il sera plus difficile de comprendre le rôle de chacun des composants… amenant donc l’effet inverse que celui escompté : des problèmes de maintenabilité, de performances, et d’évolutivité (difficile pour un développeur novice de faire évoluer correctement un code qu’il ne comprend pas !)…

     

    Voici quelques patterns que vous devriez avoir en tête en pensant « portabilité »

    • Le pattern MVVM, ou n’importe quel autre pattern de séparation entre les vues et le code métier de l’application, pour tout ce qui est interface graphique.
    • Les patterns Adapter, Bridge et Factory pour tout ce qui est séparation du code spécifique la plateforme et au matériel:
      • Les services du système d’exploitation
      • L’accès aux capteurs et au matériel d’une manière générale.

     

    En plus des design patterns, essayez de nettoyer votre code au maximum en le factorisant (ce que nous avons tendance à faire naturellement en C# mais pas forcément en XAML : hors les DataTemplates et les Styles sont là pour ça, et s’appliquer à soigner son XAML facilitera le portage des vues, qui est sans doute la plus grosse partie du travail à réaliser, comme on le verra par la suite).

     

    Il y a différents moyens de séparer les composants dans votre code. La manière que je qualifie « d’horizontale » consiste à séparer le code sous forme de couches métiers, par exemple entre les modèles objets et les objets contenant les données à afficher. Ces composants s’étagent en général de bas en haut dans un diagramme d’architecture. La manière « verticale » de séparer les composants consiste à essayer d’isoler plutôt les fonctionnalités de l’application : par exemple, la fonctionnalité « gestion des favoris » ou la fonctionnalité « comparaison des scores ».

     

    Une fois que vous avez séparé vos composants horizontalement et verticalement vous obtenez une sorte de matrice de composants. Cette matrice de composant va vous permettre d’examiner chacun des composants individuellement pour choisir la meilleure technique de portage.

    Etape 3 : Passer à l’action : découper son code sous forme de solutions/projets/namespaces pour maximiser la portabilité, et réparer.

    Dans cette étape, pas besoin de gants : on casse avant de reconstruire. Il faut prendre les morceaux de codes (qu’ils soient dans des fichiers séparés ou pas, et peu importe l’architecture actuelle du projet) et les réordonner, en fonction des stratégies de portage que vous adopterez pour chacun des composants… pas la peine, donc d’en écrire plus pour le moment. Voyons ces techniques !

     

    >> Vers la partie 3: Techniques d’adaptation et de partage de code

  • Pierre's Embedded and Mobile Blog

    Stratégies et techniques de partage de code C# et XAML entre Windows Phone et Windows 8 Release Preview : Partie 4: De la théorie à la pratique: le cas de l’application Klout Kikimeter

    • 0 Comments

    Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

    http://sdrv.ms/K4BXdN

     

    De la Théorie à la Pratique : le cas de l’Application Klout Kikimeter

     

    Klout Kikimeter est une application qui utilise les API des services Klout (http://www.klout.com) et Twitter (http://twitter.com) pour mesurer “l’influence” de l’utilisateur sur les réseaux sociaux (le "kiki" pour ceux qui ont un peu d’humour et se demandent pourquoi j’ai appelé l’application comme ça ;)).

     

    En termes de fonctionnalités, l’application propose :

    • Un composant principal qui se charge de récupérer et d’afficher le score Klout et les statistiques Twitter
    • Un système de favoris qui permet de mémoriser vos amis afin de suivre leurs scores
    • Un système de paramètres qui permet de mémoriser le nom d’utilisateur principal et d’activer ou pas la vignette dynamique
    • Un background agent permettant de mettre à jour la vignette
    • Un « générateur de vignette » qui sera utilisé par le composant principal et le background agent pour afficher le score de l’utilisateur sur la vignette de l’application dans le start screen.

     

    C’est donc une application simple mais qui illustre quand même quelques problématiques classique : modèles métiers, couche d’accès aux données sur des APIs REST/Json, nécessité d’utiliser à la fois des services du matériel et de l’OS.

     

    Etape 0 : Repenser l’expérience

     

    La première chose qui me vient en tête, c’est la différence de résolution : un écran en mode paysage plus large me permet d’avoir plus d’information en première page, c’est un luxe dont je vais profiter. Je crée donc une page qui regroupe toutes les infos utilisateurs (y compris les topics, influencers, etc) avec les favoris alignés horizontalement en haut. Cela me permettra de me servir de cette liste comme d’une forme de contrôle pivot ou tabulé pour naviguer rapidement entre les fichers de mes amis.

     

    Coté comparateur de score, je vais garder l’organisation en colonne, sauf que j’ai beaucoup plus de place. Je vais donc autoriser l’utilisateur à comparer plus de 2 personnes en ajoutant des colonnes au fur et à mesure qu’il sélectionne des utilisateurs.

     

    Pour ce qui est des paramètres, de la recherche et du partage du score sur les réseaux sociaux, je choisi de m’intégrer dans l’expérience native de Windows 8 Release Preview. Pour la vignette dynamique, je reste sur le même principe que celle de Windows Phone, avec un design Metro pour Windows 8. Et voilà, j’ai fait le tour !

     

    Etape 1: Revue d’architecture de l’application

     

    Voici le diagramme d’architecture en fonction des fonctionnalités et du découpage du code métier par rapport à la vue. Le pattern MVVM est à peu près respecté.

     

    image

     

    Première étape, refactorer les modèles objets, et la couche d’accès aux données, pour les rendre portables. Je crée donc un nouveau projet de type Portable Library qui contiendra :

    • Un dossier avec mes modèles métiers (KloutService / KloutUser)
    • Un dossier avec mes ViewModels : je les découpe suffisament pour pouvoir les composer dans différentes vues.
    • Un dossier « SystemFeatures » qui contiendra des interfaces génériques vers des services qui habituellement sont dépendants de la plateforme : on y retrouvera la gestion des paramètres de l’application, la gestion des favoris (qui dépend de l’isolated storage), une interface vers le dispatcher, et vers le background agent.

     

    Du coup, Model et ViewModels peuvent être partagés entre les projets sans problèmes ! Voici le résultat auquel j’arrive :

     

    klout kikimeter object model w8 image

     

    Théoriquement la couche d’accès aux données est partageable aussi… Si je regarde les dépendances, elle dépend de mon modèle métier (pas de soucis, on vient de voir que c’est un projet de type Portable Library) mais également d’un framework tiers : Json.NET. Heureusement l’auteur publiait, au moment de la rédaction de cet article, une version "Portable Library". Du coup, je peux aussi passer ma couche d’accès aux données dans un projet de type « Portable Library ». C’est idéal, facile à gérer dans le source control, facile à tester indépendamment de la plateforme… bref, quand on peut utiliser la portable library, on gagne sur tous les terrains.

     

    Tout le code vraiment purement spécifique à chaque plateforme (que ce soit l’accès au materiel ou au système d’exploitation) est isolé dans un projet à part que je décide de nommer « PlatformImplementation ». Le rôle de ce projet ? Implémenter les interfaces que j’ai défini dans la partie « SystemFeatures » de mon projet de type Portable Library:

    klout kikimeter platform implementation w8 image

     

    Ici, on peut séparer la gestion de source et réécrire entièrement le code, ceci étant dit, on s’apercevra souvent qu’on peut optimiser un peu : par exemple pour la gestion des favoris : si les APIs de l’isolated storage sont différentes d’une plateforme à l’autre, la sérialisation XML reste la même ! Du coup dans ce cas, je choisi d’utiliser le même fichier dans les 2 projets, et d’utiliser la compilation conditionnelle. Economie de bout de chandelle ? Peut-être, je mutualise à peine une vingtaine de ligne de code. Mais du coup, je suis sûr de ne pas faire d’oubli quand je devrai les faires évoluer. Pas de petits profits, donc ;). On parlera plus spécifiquement de ce projet dans la troisième partie de cet article, pour en voir les détails d’implémentation.

     

    Pour les vues, il n’y a malheureusement pas de magie possible. Je les recrée à la main et j’adapte les styles et les templates en premier, ce qui m’aide à construire rapidement quelque chose ayant l’identité visuelle de mon application Windows Phone. Mais pour l’architecture des composants sur la page en revanche, je pars de zéro. Ayant été relativement propre sur le pattern MVVM de ce côté-là, je n’ai quasiment pas de code-behind (quelques chargements associés à des évènements de page) que je peux copier/coller facilement. Je réutilise également les mêmes ViewModels et donc les mêmes Bindings et les mêmes IValueConverters (pour lesquels il faut juste ajuster le prototype). Pour certaines vues notamment pour démarrer sur le comparateur, j’ai quand même pu copier/coller du XAML de Windows Phone vers WinRT sans problème.

     

    klout kikimeter projects and views w8 image

     

    En ce qui concerne le Background Agent, il ne fonctionne pas du tout de la même manière dans Windows 8 Release Preview et dans Windows Phone. J’ai donc fait 2 projets séparés, avec du code séparé aussi. Cela ne me dérange pas plus que ça car le rôle d’un Background Agent c’est juste d’aller chercher le score (un appel simple à la couche d’accès aux données) et de mettre à jour la tuile (avec le générateur qui est aussi un projet séparé)… je n’ai donc que très peu de code dedans, l’impact en terme de fragmentation par plateforme est mineur. Je vais quand même générer une interface générique qui permet de l’activer et de le désactiver, histoire de pouvoir appeler des APIs unifiées depuis n’importe quelle application de la même manière : c’est utile si vous l’appelez notamment depuis un projet de type Portable Library.

     

    Comme vous pouvez le voir je n’utilise que certaines des techniques décrites précédemment pour cette application, et je me permets quelques entorses aux patterns. Chacun devra faire son choix de façon pragmatique et en fonction de ses préférences de code et de gestion de sources ! Voila le résultat final:

     

    klout kikimeter projects folded w8 image

     

    A cette étape, on a donc notre ancien code, découpé dans des nouveaux projets, et plus rien ne compile ! C’est normal, ça aurait été trop facile sinon. Il reste à « réparer » ce qui ne marche pas et notamment toutes les couches spécifiques à chaque plateforme. C’est l’objectif de l’étape 3.

     

    Etape 3 : Fixer ce qui ne marche pas dans le code existant pour coller à la nouvelle architecture

    On pourrait en dire énormément sur les équivalences entre les APIs plateformes, certains le font déjà dans des articles que j’ai mentionnés plus haut. Dans cette partie je préfère rester générique, car les APIs de WinRT ne sont pas forcément définitives : il ne s’agit que d’une Release Preview de Windows 8 ! je vais plutôt m’expliquer sur les méthodes et les questions à se poser.

     

    Dans la couche d’accès aux données : porter le code Windows Phone vers la portable library

    Mon seul problème dans cette étape, c’est l’absence d’une classe souvent utilisée dans Windows Phone mais non présente dans les projets de type Portable Library: la classe WebClient. Pour cela l’approche « wrapper » est très simple à mettre en place :il me suffit d’improviser une classe WebClient contenant uniquement le code qui m’est nécessaire et qui repose sur le couple HttpWebRequest / WebResponse. C’est une feinte pour éviter d’avoir à réécrire du code, ici, l’approche me fait largement gagner du temps (allez… au moins 1 heure ;) mais c’est une petite application, l’impact est plus important sur les couches d’accès aux données plus complexes).

    En voici la substance :

    namespace KloutKikimeter.ObjectModel.PortabilityHelpers
    {
        public class WebClient
        {
            public event DownloadStringCompletedEventHandler DownloadStringCompleted;
    
            public void DownloadStringAsync(Uri uri)
            {
                HttpWebRequest request = WebRequest.CreateHttp(uri);
                request.BeginGetResponse(state => 
                {
                    DownloadStringCompletedEventArgs dscea = new DownloadStringCompletedEventArgs();
                    try
                    {
                        var req = state.AsyncState as HttpWebRequest;
                        WebResponse response = req.EndGetResponse(state);
                        using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                        {
                            dscea.Result = sr.ReadToEnd();
                        }
                    }
                    catch (Exception ex)
                    {
                        dscea.Error = ex;
                    }
                    finally
                    {
                        if (DownloadStringCompleted != null)
                        {
                            DownloadStringCompleted(this, dscea);
                        }
                    }
                }, request);
            }
        }
    
        public delegate void DownloadStringCompletedEventHandler(object sender, DownloadStringCompletedEventArgs e);
        public class DownloadStringCompletedEventArgs : EventArgs 
        {
            public Exception Error;
            public string Result;
        }
    
    }
    

    Les favoris (un fichier dans l’isolated storage)

    Ici on cherche à masquer les APIs spécifiques sous les interfaces génériques définies dans le projet de type Portable Library. Par exemple, la portable library ne dispose pas des mots clefs async/await… hors il faut utiliser ce pattern asynchrone pour accéder à l’Isolated Storage sous Windows 8 Release Preview… ce que je fais en remontant un pattern à base d’events pour garder le coté asynchrone.

     

    namespace KloutKikimeter.ObjectModel.SystemFeatures
    {
        public interface IFavorites
        {
            event FavoritesLoadedEventHandler FavoritesLoaded;
            void LoadAsync();
    
            event EventHandler FavoritesSaved;
            void SaveAsync(IEnumerable<KloutUser> favs);
    
        }
    
        public delegate void FavoritesLoadedEventHandler(object sender, FavoritesLoadedEventArgs e);
        public class FavoritesLoadedEventArgs : EventArgs
        {
            public IEnumerable<KloutUser> Results;
        }
    }
    

    Les paramètres (settings)

     

    Pour ce qui est de la gestion des paramètres, WinRT propose un accès unifié aux paramètres depuis les “charms”, c’est à dire la petite barre vertical qui apparait à droite de l’écran quand on glisse de le doigt depuis le bord droite de l’écran, vers le centre dans Windows 8 Release Preview. WinRT propose deux « bibliothèques » pour stocker les paramètres: une bibliothèque locale (les LocalSettings) et une bibliothèque synchronisée avec le cloud (les RoamingSettings). Ces APIs prennent la forme d’un stockage clef/valeur, plus simple à utiliser que l’Isolated Storage, je réécris donc totalement le code pour tirer parti de cette fonctionnalité… Et apprendre qu’il en existe un équivalent sur Windows Phone, chose que j’ignorais, que j’implémenterai plus tard… qui améliorera la portabilité de mon code. Une méconnaissance de l’API Windows Phone aura donc un peu augmenté ma dette technique et un portage sur Windows 8 Release Preview m’aidera à la faire diminuer ! Au final, j’ai donc une interface avec 2 champs (le nom d’utilisateur et l’utilisation ou pas de la vignette dynamique) et 2 méthodes (Load et Save) qui masquent des systèmes totalement différents !

     

    namespace KloutKikimeter.ObjectModel.SystemFeatures
    {
        public interface ISettings
        {
            string TwitterUserName { get; set; }
            bool IsLiveTileAgentActive { get; set; }
    
            void Load();
            void Save();
        }
    }
    

    Le Dispatcher

     

    Autre point assez intéressant, le dispatcher. En effet, comme beaucoup je fais appel au Dispatcher au moment d’appeler l’évènement PropertyChanged du ViewModel. Hors.. ce dispatcher n’existe tout simplement pas dans un projet de type Portable Library, dans lequel j’ai pourtant mes ViewModels ! du coup, je crée une interface IDispatcher avec une méthode Invoke qui prend une Action en paramètre… et j’appelle cette interface dans mes ViewModels. Dans l’implémentation spécifique, je wrappe l’appel soit au Dispatcher Windows 8, soit au Dispatcher Windows Phone.

    Voici ci-dessous l’interface, la classe qui l’implémente, et son utilisation :

    namespace KloutKikimeter.ObjectModel.SystemFeatures
    {
        public interface IDispatcher
        {
            void Invoke(Action a);
        }
    }
    
    namespace PlatformImplementation
    {
        public class Dispatcher : IDispatcher
        {
    #if !WINDOWS_PHONE
            private CoreDispatcher cd;
    
            public Dispatcher(CoreDispatcher currentDispatcher)
            {
                cd = currentDispatcher;
            }
    #endif
    
            public void Invoke(Action a)
            {
                
    #if WINDOWS_PHONE
                    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(a);
    #else
                await cd.RunAsync(
                    Windows.UI.Core.CoreDispatcherPriority.Normal, 
                    new DispatchedHandler(a));
    #endif
            }
        }
    }
    
    namespace Klout_Kikimeter.ViewModels
    {
        public class ViewModelBase : INotifyPropertyChanged
        {
            protected IDispatcher Dispatcher;
            public ISettings Settings;
    
            public ViewModelBase(ISettings settings, IDispatcher dispatcher)
            {
                this.Settings = settings;
                this.Dispatcher = dispatcher;
            }
            
            #region INotifyPropertyChanged implementation
            public event PropertyChangedEventHandler PropertyChanged;
            public void NotifyPropertyChanged(string PropertyName)
            {
                if (PropertyChanged != null)
                    Dispatcher.Invoke(() => PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)));
            }
    
            #endregion
        }
    }
    

    Le Générateur de Vignettes Dynamiques

     

    Windows 8 Release Preview et Windows Phone proposent une fonctionnalité de “Vignette Dynamique”, mais d’un système à l’autre elles ne sont pas implémentées de la même manière, notamment la logique de « template » qui existe dans Windows 8 Release Preview n’existe pas dans Windows Phone. Encore une fois, il suffit d’implémenter une interface générique (une simple méthode GenerateTile) et d’avoir le spécifique dans un fichier dans le projet PlatformImplementation.

     

    Le Background Agent

     

    Comme dit précédemment, les Background Agents ne fonctionnent pas du tout de la même manière dans Windows 8 Release Preview et dans Windows Phone. J’ai donc deux projets de deux types différents qui ne partagent pas de code, mais qui implémentent la même interface définie dans mon projet portable library.. interface ultra simple contenant 2 méthodes : ActivateBackgroundAgent() et DeactivateBackgroundAgent().

     

    Conclusion

     

    La tâche de portage, ou de migration, de l’application Klout Kikimeter aura pris environ 3 jours, sans rien connaitre au développement d’applications Metro/WinRT. Avec le recul, si j’avais eu un niveau de connaissance suffisant en WinRT j’aurai probablement mis moins d’une journée, la tâche la plus longue étant de redéfinir les vues, ainsi que le support des résolutions multiples, ce qui d’une certaine manière peut être vu comme de l’ajout de fonctionnalité, puisque Windows Phone ne supporte qu’une résolution.

     

    Voici une évaluation “de tête” de ce que j’estime partager entre les plateformes. Bien-sûr, ces mesures variant grandement d’une application à une autre dépendant à la fois de votre manière de coder et des fonctionnalités que vous utilisez. Ceci étant dit, passer à Windows 8 Release Preview m’a donné plein d’idées pour améliorer l’application, il va donc devenir de plus en plus difficile de fournir ce genre de mesure étant donné qu’à partir de ce tronc commun, chaque version va commencer à bénéficier de spécificités propres à la plateforme

    Composant

    Code partagé

    Views

    5% (styles)

    ViewModels

    90% (exceptions are because of kludges like calling the Dispatcher)

    Models and Data Access

    100%

    Background Agent

    0% (different project)

    Platform Abstraction (Settings, Favorites, Live Tile)

    20%

    Overall

    80%

    Comme vous pourrez le constater, il n’y a donc ni moulinette automatique de code, ni règle, ni méthodes infaillible, pour réaliser cette tâche, il faut comprendre ce qu’on fait, et coller pragmatiquement aux bonnes pratiques.

     

    >> Vers la partie 5: Conclusion: Voir plus loin que la portabilité, penser à l’expérience utilisateur sur les différents terminaux. Le “continuous client”.

  • Pierre's Embedded and Mobile Blog

    Stratégies et techniques de partage de code C# et XAML entre Windows Phone et Windows 8 Release Preview : Partie 3: Techniques d’adaptation et de partage de code

    • 0 Comments

    Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

    http://sdrv.ms/K4BXdN

    Techniques de portage et de partage de code

    Entre le simple usage des mots clefs du langage, et jusqu’à des librairies spécifiques, il y a différentes manières d’architecturer le code et de minimiser la partie spécifique à chaque plateforme. Nous allons commencer avec quelques rappels sur les bases de la programmation orientée objets, puis nous avancerons dans des concepts et des outils plus complexes.

     

    Interfaces, Classes Abstraites, Classes Partielles et Méthodes d’Extension

    Les Interfaces sont l’outil le plus classique, dans le monde orienté objet, pour ajouter une couche d’abstraction, en spécifiant un “contrat” de classe sans en définir l’implémentation, qui permettrait d’avoir plusieurs classes, par exemple dans des projets différents avec les mêmes champs, propriétés et méthodes. Pour en savoir plus:

    L’avantage des interfaces, c’est qu’on peut en implémenter plusieurs dans une classe.

    Les classes abstraites peuvent être utilisées quand il faut spécifier une implementation de base qui peut être « écrasée » par des classes qui en héritent (les classes dérivées). Par rapport à une interface, on perd le multi-héritage, mais on peut y gagner une implémetnation par défaut ! Pour en savoir plus :

    Les classes partielles permettent de définir une seule classe, dans plusieurs fichiers différents. Elles sont au cœur du framework .NET et particulièrement du couple XAML/C#! Cela permet de découper la défintion d’une classe dans des fichiers séparés qui pourraient, par exemple, avoir une partie dans un projet générique, avec une autre partie dans un projet spécifique à une plateforme (à condition d’être dans le même espace de nommage). Pour en savoir plus sur les classes partielles :

    Les méthodes d’extension peuvent aussi être utilisées pour découper le code de méthodes dans différents fichiers : par exemple en étendant une classe générique avec des méthodes spécifiques à chaque plateforme. LINQ est un bon exemple d’utilisation des méthodes d’extension puisque l’ajout d’un espace de nommage et d’une référence LINQ permet d’ajouter des méthodes comme Where() et Select() aux types énumérables. Pour en savoir plus sur les méthodes d’extension:

     

    La Portable Library

     

    La portable library permet, en function des frameworks que vous ciblez, de créer des projets “portables” utilisant le sous-ensemble de classes et de méthodes commun à toutes les cibles. La portable library est inclue par défaut dans Visual Studio 11 (sauf pour la version Express) et est disponible en tant qu’extension pour Visual Studio 2010. Les frameworks cibles supportés incluent le framework .NET, WinRT, Silverlight, Silverlight pour Windows Phone, et la Xbox 360.

     

    image

     

    La portable library, de par sa nature, n’inclue pas d’APIs spécifiques à une plateforme ou un OS, mais contient tous les types de base, les fonctionnalités coeur de .NET, l’accès à Internet et au web, le parsing XML… bref, ce qu’il faut pour créer des modèles objets, et éventuellement des couches d’accès aux données. Malheureusement, très peu de frameworks d’accès aux données tiers ciblent la portable library.

     

    Une chose à garder en tête quand on conçoit l’architecture d’une application : un projet ciblant la portable library ne peut pas référencer de projets qui ciblent des plateformes spécifiques. Cela peut paraitre évident, pour rester indépendant justement, mais il en découle des implications importantes, notamment le fait que la portable library ne peut pas servir de couche intermédiaire entre deux composants spécifiques à une plateforme, ou bien de couche d’abstraction à des services du système d’exploitation par exemple. Pour en savoir plus à propos de la portable library :

     

    Voici également une belle série d’articles par Stéphanie Hertrich qui détaille l’utilisation de cette portable library WinRT, Windows Phone et Silverlight 5 :

     

    Ecrire son propre wrapper.

     

    La plupart du temps on écrit du code sans penser à la portabilité… en utilisant des classes de haut niveau qui n’existent que sur une plateforme plutôt qu’une classe équivalente qui serait portable. C’est le cas, par exemple, de la classe WebClient, spécifique à Windows Phone, alors qu’il existe la classe HttpWebRequest qui est portable et qui fait la même chose (voire plus, en fait). Dans ces cas-là, 2 solutions : soit réécrire le code, soit ré-implémenter la classe manquante au-dessus de celles qui existent dans le nouveau framework… une sorte de wrapper. L’avantage est que cela permet de ne pas toucher du tout au code existant.

     

    ATTENTION : n’utilisez pas de désassembleurs comme Reflector ou DotPeek, pour « récupérer » une classe dans un framework et la porter dans un autre. C’est une mauvaise pratique !

     

    La compilation conditionnelle, les liens et les directives de préprocesseur

     

    La “compilation conditionnelle” peut être utilisée quand on veut mélanger, dans le même fichier, du code spécifique à une plateforme, et du code générique. Ensuite il suffit d’ajouter ce fichier à tous les projets, en utilisant le bouton « ajouter un lien » dans la fenêtre d’ajout de nouveaux éléments.

     

    image

     

    Chacun se fera son idée de la “propreté” de cette méthode, en ce qui me concerne j’apprécie la clarté du découpage entre le code spécifique et le code générique, cela permet de voir facilement comment maximiser la réutilisation de l’une ou de l’autre.

    image

    [Attention] Les gestionnaires de sources ne prennent pas en compte les liens entre les fichiers (seuls les “vrais” fichiers sont commité. Il faut donc que les développeurs aient accès aux projets de toutes les plateformes s’ils lient dans leur projets des fichiers d’autres projets.

     

    L’utilisation de la CTP Async pour Visual Studio

    Toutes les opérations pouvant prendre plus de 50ms dans WinRT sont asynchrones dans Windows 8 Release Preview, et font usage des mots clefs async et await. Ce n’est pas le cas dans Windows Phone, mais il est possible de rajouter cette fonctionnalité et ces mots clefs dans le runtime Windows Phone à l’aide de l’Async CTP. Même s’il s’agit d’une CTP, donc non supportée, elle est pour autant suffisamment stable pour être utilisée dans une application. Cela permet de réutiliser les mêmes API et les mêmes patterns de code sur les deux plateformes, et ça peut être particulièrement utile sur tout ce qui concerne l’abstraction du hardware ou du réseau (celles-ci étant quasi systématiquement asynchrones). Vous pouvez trouver plus d’informations sur la CTP Async avec le lien suivant :

     

    Attention, la CTP Async n’est pas installable sur une machine avec le framework .NET 4.5, Visual Studio 11 ou Windows 8 Release Preview !

     

    L’utilisation de frameworks multi-plateformes

    La plupart des développeurs font appel à des frameworks tiers pour simplifier le développement d’applications et compléter les APIs du Framework .NET : par exemple, pour parser du JSON, pour s’interfacer aux APIs des réseaux sociaux, ou pour implémenter simplement des patterns. Dans le cas où vous utilisez des frameworks tiers, assurez-vous que leurs dépendances restent dans les APIs tolérées par WinRT et les applications de type Metro (qui n’ont pas accès à l’intégralité du framework .NET 4.5).

     

    XAML: Utiliser les Styles et les DataTemplates

    En ce qui concerne la description des interfaces, en XAML, il faut essayer de distinguer au maximum le découpage de la page (la façon d'empiler et de situer les éléments) des éléments purement graphiques : choix des couleurs, des polices de caractères, etc. Plus cette separation sera claire, plus il sera simple de réimporter la charte graphique sur une autre plateforme tout en réarrangeant, indépendament les éléments sur la page pour utiliser l’intégralité de l’écran par exemple, ce qu’il faut absolument faire quand on change de facteur de forme et donc d’ergonomie.

     

    Une dernière chose sur l’injection de dépendances, MEF et Unity

    Certains outils d’injection de dépendances comme MEF et Unity auraient pu permettre de découpler certaines parties du code, en faisant encore une fois usage d’interfaces et de bouts de code plateforme spécifiquement séparés et chargés dynamiquement au démarrage. Malheureusement, le Managed Extensibility Framework (MEF) n’est pas supporté dans Windows Phone. Il est donc d’une utilité très limitée dans le cas qui nous occupe…

    >> Vers la partie 4: De la théorie à la pratique: le cas de l’application Klout Kikimeter

  • Pierre's Embedded and Mobile Blog

    Stratégies et techniques de partage de code C# et XAML entre Windows Phone et Windows 8 Release Preview : Partie 5: Voir plus loin que la portabilité

    • 0 Comments

    Cette (longue) série d’articles est découpée en plusieurs billets sur ce blog, mais disponible également dans un seul document ici:

    http://sdrv.ms/K4BXdN

    Voir plus loin que la portabilité: penser l’expérience utilisateur sur les différents terminaux : le paradigme du “continuous client”

    Si votre utilisateur apprécie votre application il y a de fortes chances qu’il cherche à la retrouver sur les différents terminaux qu’il possède. Cet utilisateur s’attendra au minimum à trouver une expérience taillée pour chaque facteur de forme, au mieux, à pouvoir importer et partager ses paramètres, ses favoris, entre ces différents terminaux.

    Par exemple, dans l’application Klout qu’on vient de voir, il est tout à fait raisonnable d’imaginer que les favoris et le ranking soit partagés, par exemple à travers un service Cloud, entre l’application téléphone et l’application tablette.

    Si la synchronisation des données est la base de l’idée de continuous client, on peut aller plus loin, en imaginant que l’utilisateur pourra « continuer » l’activité qu’il avait sur un terminal directement sur un autre terminal de façon fluide. Par exemple, pour une application autour du cinéma, consulter des fiches de film dans le métro sur son téléphone, et pouvoir retrouver et regarder les bandes annonces des films qu’il a sélectionné dans le métro une fois chez lui, sur sa tablette. Dans toutes ces expériences le rôle du Cloud va devenir prépondérant, donc si vous n’êtes pas familiers avec les concepts d’Azure, par exemple (au dela de l’hébergement ou du stockage, tout ce qui est fédération d’identité, scénarios de synchro online/offline, etc) je vous encourage à y jeter un œil pour comprendre à quel point le cloud pourra enrichir votre application. Il y a d’ailleurs pour Windows Azure un toolkit qui simplifie le développement de backends pour WinRT, Windows Phone mais aussi disponible sur iOS et Android, et vous pouvez le trouver sur codeplex et sur github :

Page 1 of 1 (5 items)