{"id":660,"date":"2014-06-25T23:43:37","date_gmt":"2014-06-25T21:43:37","guid":{"rendered":"http:\/\/dev.bratched.fr\/fr\/?p=660"},"modified":"2014-06-25T23:43:37","modified_gmt":"2014-06-25T21:43:37","slug":"images-et-isolated-storage-windows-phone-8","status":"publish","type":"post","link":"https:\/\/bratched.com\/fr\/2014\/06\/25\/images-et-isolated-storage-windows-phone-8\/","title":{"rendered":"Isolated Storage et Images sous Windows Phone 8"},"content":{"rendered":"<p>Je vous propose un petit tour d&rsquo;horizon sur le stockage d&rsquo;images dans l&rsquo;Isolated Storage.<\/p>\n<p>Pour rappel, l&rsquo;Isolated storage est l&#8217;emplacement r\u00e9serv\u00e9 \u00e0\u00a0votre application Windows Phone pour\u00a0le stockage de fichiers.<\/p>\n<p>Quelles sont les impl\u00e9mentations qui permettent de r\u00e9cup\u00e9rer une image d\u00e9j\u00e0 stock\u00e9e dans cet espace ? Quelles sont les m\u00e9thodes les plus efficaces ?<\/p>\n<h1>L&rsquo;impl\u00e9mentation classique (Windows Phone 7)<\/h1>\n<p>C&rsquo;est l&rsquo;impl\u00e9mentation standard \u00ab\u00a0old school\u00a0\u00bb : j&rsquo;utilise GetUserStoreForApplication() et OpenFile pour lire un Stream. Le Stream est ensuite lu par le BitmapImage et renvoy\u00e9 dans une Action.<\/p>\n<pre class=\"lang:default decode:true\">public static void LoadImageFromStorage(string name, Action&lt;BitmapImage&gt; actionBitmap)\n{\n    using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())\n    {\n        if (myIsolatedStorage != null &amp;&amp; myIsolatedStorage.FileExists(name))\n        {\n            IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(name, FileMode.Open, FileAccess.Read);\n            {\n                Deployment.Current.Dispatcher.BeginInvoke(() =&gt;\n                {\n                    BitmapImage bitmapSource = new BitmapImage();\n                    bitmapSource.SetSource(fileStream);\n                    if (actionBitmap != null)\n                        actionBitmap(bitmapSource);\n                });\n            }\n        }\n    }\n}<\/pre>\n<h2>Remarque 1<\/h2>\n<p>BitmapImage n\u00e9cessite de s&rsquo;ex\u00e9cuter dans le ThreadUI principal, pour cela, on utilise<\/p>\n<pre class=\"lang:default decode:true\">Deployment.Current.Dispatcher.BeginInvoke(() =&gt;\n<\/pre>\n<p><!--more--><\/p>\n<h2>Remarque 2<\/h2>\n<p>Le Stream de l&rsquo;isolated Storage\u00a0peut s&rsquo;ex\u00e9cuter dans un Thread en arri\u00e8re plan, on ne peut pas ici utiliser une syntaxe comme celle ci dessous (qui permettrait de garantir la lib\u00e9ration m\u00e9moire du FileStream).<\/p>\n<pre class=\"lang:default decode:true\">using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(name, FileMode.Open, FileAccess.Read))\n{\n...\n}<\/pre>\n<p>Attention donc, car l&rsquo;utilisation du using fermerait le FileStream avant que le ThreadUI ne puisse l&rsquo;utiliser.<\/p>\n<h2>Utilisation de la m\u00e9thode<\/h2>\n<p>L&rsquo;utilisation de cette premi\u00e8re m\u00e9thode pourrait \u00eatre celle ci :<\/p>\n<pre class=\"lang:default decode:true\">IsolatedImageService.LoadImageFromStorage(\"TestImage.png\", b =&gt;\n{\n    Image = (b as BitmapImage);\n});<\/pre>\n<p>(o\u00f9 image repr\u00e9sente\u00a0une propri\u00e9t\u00e9 de type BitmapImage)<\/p>\n<p>&nbsp;<\/p>\n<h1>L&rsquo;impl\u00e9mentation Windows Phone 8 (async \/ await)<\/h1>\n<p>Windows Phone 8 permet l&rsquo;ancienne syntaxe Windows Phone 7, mais ajoute \u00e9galement\u00a0tout une gestion de l&rsquo;Isolated Storage avec\u00a0la nouvelle classe StorageFile.<\/p>\n<p>La classe StorageFile va proposer des actions asynchrones\u00a0assez efficaces.<\/p>\n<p>Ici, j&rsquo;utilise GetFileFromApplicationUriAsync pour r\u00e9cup\u00e9rer en une seule instruction le fichier image de l&rsquo;Isolated Storage.<\/p>\n<p>Le Stream est ensuite accessible avec OpenStreamForReadAsync();<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;BitmapImage&gt; LoadImageFromStorage2(string name)\n{\n    Windows.Storage.StorageFile storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(\"ms-appdata:\/\/\/local\/\" + name));\n    BitmapImage image = null;\n    using (Stream stream = await storageFile.OpenStreamForReadAsync())            \n    {\n        using (MemoryStream ms = new MemoryStream())\n        {\n            await stream.CopyToAsync(ms);\n            await Task.Run(() =&gt;\n            {\n                synchroUI.Send(_ =&gt;\n                {\n                    image = new BitmapImage();                    \n                    image.SetSource(ms);\n                }, null);\n            });\n        }             \n    }\n    return image;\n}<\/pre>\n<h2>Remarque 1<\/h2>\n<p>Le code suivant :<\/p>\n<pre class=\"lang:default decode:true\">DispatcherSynchronizationContext synchroUI = new DispatcherSynchronizationContext;\n...\n\nawait Task.Run(() =&gt;\n            {\n                synchroUI.Send(_ =&gt;\n                {\n<\/pre>\n<p>permet d&rsquo;utiliser l&rsquo;UIThread principal, obligatoire pour l&rsquo;utilisation de BitmapImage. Il peut \u00eatre remplac\u00e9 par<\/p>\n<pre class=\"lang:default decode:true\">Deployment.Current.Dispatcher.BeginInvoke(() =&gt;\n<\/pre>\n<h2>Remarque 2<\/h2>\n<p>L&rsquo;utilisation du MemoryStream permet d&rsquo;\u00e9viter une fuite m\u00e9moire qui a lieu lorsque le Stream de l&rsquo;IsolatedStorage reste bloqu\u00e9 dans le Thread de t\u00e2che de fond.<\/p>\n<p>C&rsquo;est le flux de donn\u00e9es copi\u00e9 dans le MemoryStream qui va ensuite \u00eatre\u00a0utilis\u00e9 par le BitmapImage.<\/p>\n<pre class=\"lang:default decode:true\"> using (MemoryStream ms = new MemoryStream())\n        {\n            await stream.CopyToAsync(ms);\n<\/pre>\n<h2>Remarque 3<\/h2>\n<p>Il est possible d&rsquo;utiliser la syntaxe Using sur les Stream sans g\u00e9n\u00e9rer de fuites m\u00e9moire:<\/p>\n<pre class=\"lang:default decode:true\"> using (Stream stream = await storageFile.OpenStreamForReadAsync())\n<\/pre>\n<h2>Appel de la m\u00e9thode<\/h2>\n<p>La m\u00e9thode peut alors \u00eatre appel\u00e9e de la fa\u00e7on suivante\u00a0 :<\/p>\n<pre class=\"lang:default decode:true\">Image = await IsolatedImageService.LoadImageFromStorage2(\"TestImage.png\");\n<\/pre>\n<p>(o\u00f9 Image est une propri\u00e9t\u00e9 de type BitmapImage)<\/p>\n<h1>Une autre implementation async await Windows Phone 8<\/h1>\n<p>Il existe une variante du code pr\u00e9c\u00e9dent avec l&rsquo;utilisation d&rsquo;un IRandomAccessStream et du ReadAsync.<\/p>\n<pre class=\"lang:default decode:true\">public static async Task&lt;BitmapImage&gt; LoadImageFromStorage3(string name)\n{\n    Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;\n    Windows.Storage.StorageFile storageFile = await localFolder.GetFileAsync(name);\n    BitmapImage image = null;\n    IRandomAccessStream accessStream = await storageFile.OpenReadAsync();\n    using (Stream stream = accessStream.AsStreamForRead((int)accessStream.Size))\n    {\n        byte[] content = new byte[stream.Length];\n        await stream.ReadAsync(content, 0, (int)stream.Length);\n        await Task.Run(() =&gt;\n        {\n            synchroUI.Send(_ =&gt;\n            {\n                image = new BitmapImage();\n                using (var ms = new MemoryStream(content))\n                {                            \n                    image.SetSource(ms);                            \n                } \n            }, null);\n        });\n        stream.Close();\n    }\n    return image;\n}<\/pre>\n<p>Le ReadAsync lit les donn\u00e9es dans un buffer et utilise un MemoryStream pour son injection dans BitmapImage.<\/p>\n<h1>Quelle m\u00e9thode est la plus rapide ?<\/h1>\n<p>J&rsquo;ai repris le programme de Benchmark et test\u00e9 nos 3 m\u00e9thodes en chargeant une image importante (400ko, 1000 pixels x 1000 pixels) 100 fois.<\/p>\n<p><a href=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/06\/wp_ss_20140628_0001.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-674\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/06\/wp_ss_20140628_0001.png\" alt=\"Benchmark Screenshot\" width=\"550\" height=\"916\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/06\/wp_ss_20140628_0001.png 768w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/06\/wp_ss_20140628_0001-180x300.png 180w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/06\/wp_ss_20140628_0001-614x1024.png 614w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><\/a><\/p>\n<p>Voici les r\u00e9sultats (moyennes apr\u00e8s une dizaine de tests cons\u00e9cutifs pour 100 appels):<\/p>\n<ul>\n<li>M\u00e9thode 1 (Action, syntaxe wp7\u00a0) : <span style=\"color: #339966\">8,5 secondes<\/span><\/li>\n<li>M\u00e9thode 2 (WP8 avec GetFileFromApplicationUriAsync : <span style=\"color: #ff0000\">11 secondes<\/span><\/li>\n<li>M\u00e9thode 3 (WP8 avec\u00a0IRandomAccessStream et ReadAsync\u00a0:\u00a0<span style=\"color: #ff9900\">9,5 secondes<\/span><\/li>\n<\/ul>\n<p>Avec ce test, on voit que l&rsquo;utilisation de IRandomAccessStream avec Async \/ Await\u00a0\u00a0n&rsquo;est pas\u00a0tr\u00e8s \u00e9loign\u00e9e d&rsquo;une utilisation avec le simple Action.<\/p>\n<p>Les 3 m\u00e9thodes restent cependant incroyablement longues par rapport \u00e0 <a title=\"Utilisation des Images resources avec Windows Phone\" href=\"http:\/\/dev.bratched.fr\/fr\/utilisation-des-images-resources-avec-windows-phone\/\">l&rsquo;utilisation directe d&rsquo;une ressource de l&rsquo;application<\/a>.(0,3 s).<\/p>\n<p>Conclusion : Privil\u00e9giez toujours l&rsquo;utilisation des ressources lorsque c&rsquo;est possible.<\/p>\n<p>Le code source est ici :<\/p>\n<p><a title=\"Code source sur Images et IsolatedStorage\" href=\"http:\/\/1drv.ms\/TFUN1c\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-633\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256.png\" alt=\"download-10-icon-256\" width=\"256\" height=\"256\" srcset=\"https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256.png 256w, https:\/\/bratched.com\/fr\/wp-content\/uploads\/sites\/2\/2014\/05\/download-10-icon-256-150x150.png 150w\" sizes=\"(max-width: 256px) 100vw, 256px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Je vous propose un petit tour d&rsquo;horizon sur le stockage d&rsquo;images dans l&rsquo;Isolated Storage. Pour rappel, l&rsquo;Isolated storage est l&#8217;emplacement r\u00e9serv\u00e9 \u00e0\u00a0votre application Windows Phone pour\u00a0le stockage de fichiers. Quelles sont les impl\u00e9mentations qui permettent de r\u00e9cup\u00e9rer une image d\u00e9j\u00e0 stock\u00e9e dans cet espace ? Quelles sont les m\u00e9thodes les plus efficaces ? L&rsquo;impl\u00e9mentation classique [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[63],"tags":[68,69,83,70,9,84,82,72],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/660"}],"collection":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/comments?post=660"}],"version-history":[{"count":0,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/660\/revisions"}],"wp:attachment":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/media?parent=660"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/categories?post=660"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/tags?post=660"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}