<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Le blog d&#039;un développeur</title>
	<atom:link href="https://blog.developpez.com/ndruet/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/ndruet</link>
	<description></description>
	<lastBuildDate>Fri, 03 May 2013 09:23:00 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Les librairies disponibles pour Android</title>
		<link>https://blog.developpez.com/ndruet/p8709/android/les_librairies_disponibles_pour_android</link>
		<comments>https://blog.developpez.com/ndruet/p8709/android/les_librairies_disponibles_pour_android#comments</comments>
		<pubDate>Mon, 15 Mar 2010 15:02:08 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[[ Mise à jour : 24/06/2010 ] Les librairies pour le développement de jeux ou d&#8217;applications Android ne sont pas nombreuses mais surtout peu connues. On trouve néanmoins des moteurs 2D, des API de réalité augmentée, des API de lecture/génération de code-barres, d&#8217;autres plus généralistes&#8230; En voici une liste qui ne demande qu&#8217;à être mise à jour. Elles sont triées en fonction de leur licence d&#8217;utilisation. > Licence libre Box2D Moteur physique 2D complet et [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><ins>[ Mise à jour : 24/06/2010 ]</ins></p>
<p>Les librairies pour le développement de jeux ou d&rsquo;applications Android ne sont pas nombreuses mais surtout peu connues. On trouve néanmoins des moteurs 2D, des API de réalité augmentée, des API de lecture/génération de code-barres, d&rsquo;autres plus généralistes&#8230;</p>
<p>En voici une liste qui ne demande qu&rsquo;à être mise à jour. Elles sont triées en fonction de leur licence d&rsquo;utilisation.</p>
<p><span id="more-7"></span><br />
<br />
<strong>> Licence libre</strong></p>
<ul>
<li><strong>Box2D</strong><br />
<table>
<tr>
<td><img src="http://blog.developpez.com/media/box2d.png"/></td>
<td>Moteur physique 2D complet et libre. Gestion de la physique des corps, détecteur de collisions&#8230; A L&rsquo;origine écrit en C++ par Erin Catto, elle a été portée en Java mais aussi en Flash.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.box2d.org/<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://www.4feets.com/2009/03/2d-physics-on-android-using-box2d/
</td>
</tr>
</table>
</li>
<li>
<strong>Angle</strong></p>
<table>
<tr>
<td><img src="http://blog.developpez.com/media/angle.png"/></td>
<td>
Moteur 2D OpenGL pour développer de jeux. Apporte également la gestion du son. Une série du tutoriels est fournie dans l&rsquo;archive à télécharger.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/angle/
</td>
</tr>
</table>
</li>
<li><strong>Rokon</strong><br />
<table>
<tr>
<td><img src="http://blog.developpez.com/media/rokon.png"/></td>
<td>Moteur 2D relativement complet avec le support des capteurs. Un forum, des tutoriels et un outil de rapport de bugs sont présents.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://rokonandroid.com/
</td>
</tr>
</table>
</li>
<li>
<strong>Zxing (Zebra Crossing)</strong></p>
<table>
<tr>
<td><img src="http://blog.developpez.com/media/zxing.png"/></td>
<td>Lecteur de code-barres 1D et 2D (dont QR Code) via l&rsquo;APN du téléphone. A l&rsquo;origine développée en Java, la librairie (sous licence Apache) est également disponible pour Iphone et Blackberry.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/zxing/<br />
<code class="codecolorer text default"><span class="text">Ressources :</span></code></p>
<p>http://www.insideandroid.fr/post/2009/04/23/ZXing-%3A-Comment-tester-le-scan-de-code-QR-sur-lemulateur</p>
<p>http://www.insideandroid.fr/post/2009/04/16/Tutorial-%3A-Scanner-et-geacuteneacuterer-des-codes-QR-2D-sur-Android</p>
</td>
</tr>
</table>
</li>
<li><strong>AndroidDataFramework</strong><br />
Framework de gestion (création, accès&#8230;) de base de données SQLite à l&rsquo;aide de fichiers XML.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/androiddataframework/<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://www.brighthub.com/mobile/google-android/articles/52883.aspx</p>
</li>
<li><strong>DroidCouch</strong><br />
Librairie d&rsquo;accès à la base de données SQLite. Elle se veut minimaliste. Sous license MIT.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/sig/DroidCouch</p>
</li>
<li><strong>Scala-android</strong><br />
Pour développer en Scala sous Android. &laquo;&nbsp;<em>Scala est un langage de programmation multi-paradigme conçu à l&rsquo;EPFL pour exprimer les modèles de programmation courants dans une forme concise et élégante.</em>&nbsp;&raquo; <a href="http://fr.wikipedia.org/wiki/Scala_%28langage%29">Wikipedia</a>.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/scala-android/<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://www.akshaydashrath.com/2009/10/scala-and-android.html</p>
</li>
<li>
<strong>aSmack (XMMP)</strong><br />
Permet aux applications de se connecter facilement aux serveurs de messagerie instantanée Jabber/XMPP. C&rsquo;est un portage non officiel sous Android de Smack API. Vous trouverez tout le support nécessaire sur le site officiel http://www.igniterealtime.org/projects/smack/ pour la version standard.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/asmack/</p>
</li>
<li><strong>Calculaton</strong><br />
Calculaton est un framework de tests d&rsquo;<a href="http://developer.android.com/intl/fr/reference/android/app/Activity.html">Activity</a> avec une syntaxe relativement naturelle pour ceux qui connaissent JUnit.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/kaeppler/calculon/<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://brainflush.wordpress.com/2010/01/10/introducing-calculon-a-java-dsl-for-android-activity-testing/
</li>
<li><strong>CW-Android</strong><br />
Collection de codes (sous licence Apache) concernant la gestion des Activity, des fonts, de la base de données, des threads&#8230; tous issus du livre <a href="http://www.amazon.fr/Busy-Coders-Guide-Android-Development/dp/0981678009">The Busy Coder&rsquo;s Guide to Android Development!</a> dont la traduction en Français s&rsquo;appelle <a href="http://www.frandroid.com/6547/livre-lart-du-developpement-android/">L&rsquo;art du développement Android</a>.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/commonsguy/cw-android<br />
<code class="codecolorer text default"><span class="text">Livres :</span></code><br />
<a href="http://www.amazon.fr/Busy-Coders-Guide-Android-Development/dp/0981678009">The Busy Coder&rsquo;s Guide to Android Development!</a><br />
<a href="http://www.frandroid.com/6547/livre-lart-du-developpement-android/">L&rsquo;art du développement Android</a></p>
</li>
<li>
<strong>Droid-Fu</strong><br />
Librairie généraliste, sous licence Apache, apportant un support pour la gestion du cycle de vie des applications, les tâches de fonds, les requêtes HTTP&#8230; et propose également des views prédéfinies.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/kaeppler/droid-fu<br />
<code class="codecolorer text default"><span class="text">Ressources :</span></code></p>
<p>http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/</p>
<p>http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/</p>
</li>
<li>
<strong>OpenSocial Java Client Library</strong><br />
Support d&rsquo;<a href="http://fr.wikipedia.org/wiki/OpenSocial">OpenSocial</a> dans vos applications. <a href="http://code.google.com/p/opensocial-java-client/wiki/GettingStarted">La documentation</a> est complète et bien faite.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/opensocial-java-client/</p>
</li>
<li>
<strong>RESTProvider</strong><br />
Pour effectuer des requêtes HTTP. Gère les formats JSON et XML pour les réponses.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/novoda/RESTProvider</p>
</li>
<li><strong>XMLRCP</strong><br />
Pour réaliser des appels distants de méthodes via le protocole XML-RPC.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://code.google.com/p/android-xmlrpc/</p>
</li>
<li><strong>GreenDroid</strong><br />
<table>
<tr>
<td><img src="http://blog.developpez.com/media/greendroid.png"/></td>
<td>GreenDroid est une libraire graphique française (par Cyril Mottier). Ses objectifs sont multiples : cohérence entre les applications, rapidité d&rsquo;exécution, utilisation de concepts avancés, facilité d&rsquo;utilisation, etc. C&rsquo;est encore un travail en cours mais elle dispose déjà de fonctionnalités intéressantes comme les listes basées sur les items, la création de liste avancés par XML, etc.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/cyrilmottier/GreenDroid<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://android.cyrilmottier.com/?p=240
</td>
</tr>
</table>
</li>
<li><strong>Facebook</strong><br />
<table>
<tr>
<td><img src="http://blog.developpez.com/media/facebook.gif"/></td>
<td>
Le SDK Android de Facebook regroupe l&rsquo;authentification des utilisateurs via OAuth 2.0, les appels aux APIs Facebook et Open Graph ainsi que l&rsquo;intégration des applications Facebook.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://github.com/facebook/facebook-android-sdk
</td>
</tr>
</table>
</li>
</ul>
<p></p>
<p><strong>> Licence développeur gratuites</strong></p>
<ul>
<li><strong>Photoshop Mobile Editor</strong></p>
<table>
<tr>
<td><img src="http://blog.developpez.com/media/photoshop.png"/></td>
<td>Librairie officielle Adobe permettant l&rsquo;intégration de l&rsquo;éditeur de photos dans vos applications Android, et cela gratuitement.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://mobile.photoshop.com/android/developers.html
</td>
</tr>
</table>
</li>
<li>
<strong>Wikitude</strong></p>
<table>
<tr>
<td><img src="http://blog.developpez.com/media/wikitude.png"/></td>
<td>Une API de Réalité Augmentée complète et simple d&rsquo;utilisation. Elle est également disponible sur IPhone. Pour enlever le bandeau &laquo;&nbsp;beta&nbsp;&raquo; de votre application, vous devrez simplement vous inscrire sur le site. La documentation est complète et un forum existe pour faire part de votre expérience.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.wikitude.org/developers<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://android-france.fr/tag/api-wikitude/
</td>
</tr>
</table>
</li>
</ul>
<p></p>
<p><strong>> <ins>$</ins> Licence développeur payante</strong></p>
<ul>
<li><strong>Android Barcode</strong><br />
Génère tout type de code-barres qu&rsquo;ils soient sous forme de lignes (1D) ou de matrices (2D). La documentation est relativement complète. Les versions IPhone et Windows mobiles existent également. Une version d&rsquo;essai est disponible.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.onbarcode.com/products/android_barcode/</p>
</li>
<li><strong>J4L Barcode</strong><br />
Génère des code-barres 2D de type QRCode et Datamatrix (seulement :(). Une version d&rsquo;essai est disponible.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.java4less.com/barcodes/barcodes.php</p>
</li>
<li><strong>J4L RChart</strong><br />
Génère toute sorte de graphiques tels que les camemberts, les histogrammes&#8230; Les deux aperçus d&rsquo;écrans trouvés ne donnent pas très envie. Une version d&rsquo;essai est disponible.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.java4less.com/charts/chart.php?info=android<br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> http://android-france.fr/2010/03/02/developpez-des-diagrammes-de-barres-statistiques-dans-vos-applications-android/</p>
</li>
<li><strong>Android Chart</strong><br />
Génère toute sorte de graphiques tels que les camemberts, les histogrammes&#8230; Le &laquo;&nbsp;Developer Guide&nbsp;&raquo; n&rsquo;est pas rédigé (statut: Coming soon) que ce soit pour la version IPhone ou Android. Çela manque cruellement de sérieux. Une version d&rsquo;essai est disponible.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.keepedge.com/products/android_charting/</p>
</li>
<li><strong>aiCharts</strong><br />
<table>
<tr>
<td><img src="http://blog.developpez.com/media/aichart.png"/></td>
<td>Génère toute sorte de graphiques tels que les camemberts, les histogrammes, les pyramides&#8230; pour toutes sorte de données (financières, commerciales, techniques, mathématiques&#8230;). Le site rassure avec de très jolis aperçus d&rsquo;écrans, une documentation complète et des exemples de cas d&rsquo;utilisations. Une version d&rsquo;essai est disponible.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> http://www.artfulbits.com/Android/aiCharts.aspx
</td>
</tr>
</table>
</li>
<li>
<strong>PayPal Mobile Payment Libraries</strong></p>
<table>
<tr>
<td><img src="http://blog.developpez.com/media/paypal.png"/></td>
<td>L&rsquo;API PayPal permet d&rsquo;intégrer le célèbre service de paiement sécurisé dans son application.<br />
<code class="codecolorer text default"><span class="text">Site :</span></code> <a href="https://www.x.com/community/ppx/xspaces/mobile">https://www.x.com/community/ppx/xspaces/mobile</a><br />
<code class="codecolorer text default"><span class="text">Ressource :</span></code> <a href="https://www.x.com/community/ppx/xspaces/mobile/mep">https://www.x.com/community/ppx/xspaces/mobile/mep</a>
</td>
</tr>
</table>
</li>
</ul>
<p>
Le Site <a href="http://www.openintents.org/en/libraries">Openintents.org</a> en référence également quelques une.</p>
<p>Avant de choisir une librairie, il est important de vérifier qu&rsquo;il existe bien une documentation ou un guide de démarrage. Cela facilitera la prise en main. Vérifier également que des mises à jour régulières sont effectuées. Si vous rencontrez des bugs, vous pouvez espérer qu&rsquo;ils soient corrigés dans les prochaines versions. Un forum n&rsquo;est pas inutile pour faire part de vos problèmes. S&rsquo;il y en a pas, vous trouverez peut être des réponses sur <a href="http://www.developpez.net/forums/f1238/java/general-java/java-mobiles/android/">le forum Android de développer.com</a>. Dernière vérification, la présence de tutoriels sur le net indique si la librairie a déjà suscité un intérêt ou non.</p>
<p>Toutes les librairies payantes proposent une version d&rsquo;essai. N&rsquo;hésitez pas à l&rsquo;utiliser pour vous assurer de la compatibilité avec votre version d&rsquo;Android. A l&rsquo;exception d&rsquo;<a href="http://www.artfulbits.com/Android/aiCharts.aspx">aiCharts</a>, et d&rsquo;<a href="http://www.onbarcode.com/">Android Barrecord</a>, aucune ne donne réellement envie d&rsquo;y investir le moindre euros/dollar. Les sites sont soit old-school, soit la documentation est inexistante, soit les deux pour certains. Tout cela manque de sérieux.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rendre son application compatible avec tous les Android-phones (ou presque)</title>
		<link>https://blog.developpez.com/ndruet/p8665/android/rendre_son_application_comptible_avec_to</link>
		<comments>https://blog.developpez.com/ndruet/p8665/android/rendre_son_application_comptible_avec_to#comments</comments>
		<pubDate>Wed, 17 Mar 2010 09:50:51 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Les développeurs Iphone ont bien de la chance. Les 3 téléphones d&#8217;Apple ont des caractéristiques très proches: résolutions d&#8217;écrans identiques (11,5 x 6,1 x 1,16), des capteurs identiques (gps, boussoles magnétiques,accéléromètres&#8230;), le WiFi&#8230; L&#8217;OS se met à jour via ITunes et permet ainsi aux utilisateurs de bénéficier des dernières nouveautés immédiatement après la publication d&#8217;Apple. Malheureusement, les développeurs Android sont beaucoup moins bien lotis. En début d&#8217;année Google publiait la répartition des versions de son [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Les développeurs Iphone ont bien de la chance. Les 3 téléphones d&rsquo;Apple ont des caractéristiques très proches: résolutions d&rsquo;écrans identiques (11,5 x 6,1 x 1,16), des capteurs identiques (gps, boussoles magnétiques,accéléromètres&#8230;), le WiFi&#8230; L&rsquo;OS se met à jour via ITunes et permet ainsi aux utilisateurs de bénéficier des dernières nouveautés immédiatement après la publication d&rsquo;Apple.</p>
<p>Malheureusement, les développeurs Android sont beaucoup moins bien lotis. En début d&rsquo;année Google publiait <a href="http://developer.android.com/resources/dashboard/platform-versions.html">la répartition des versions de son OS</a>. Prêt de la moitié des clients de l&rsquo;Android Market tournent en version 1.6, 30% en 1.5 et seulement 20% en version 2.0. Mais ce n&rsquo;est pas le seul problème. Le nombre de modèles de téléphones aux caractéristiques totalement différentes augmente de plus en plus. Si bien que cibler un modèle et une version de l&rsquo;OS n&rsquo;est pas une stratégie gagnante. Il suffit d&rsquo;aller sur <a href="http://romandroid.ch/discussions/parts-de-marche-des-telephones-android-en-europe-janvier-2010">romandroid.ch</a> pour s&rsquo;en convaincre d&rsquo;avantage. Quelque soit le pays d&rsquo;Europe aucun téléphone ne domine le marché, les résolutions d&rsquo;écrans sont très différentes ainsi que les versions d&rsquo;Android. Quant à la mise à jour de l&rsquo;OS, elle dépend du bon vouloir de l&rsquo;opérateur ou du constructeur. Difficile dans ces conditions d&rsquo;avoir un parc de téléphones unifié.</p>
<p>Après ce constat, qui pourrait en décourager plus d&rsquo;un, on peut se demander ce que fait Google. Et bien rien, puisque son OS pour mobile a été conçu dans le but d&rsquo;être installé sur le plus grand nombre d&rsquo;appareils possible. Mais je vous rassure, il existe des solutions. Dans ce post, nous parcourons quelques-une d&rsquo;entre elles :</p>
<ul>
<li><a href="#os">Gérer les versions d&rsquo;Android</a></li>
<li><a href="#ecrans">Supporter les différents écrans</a>
<ul>
<li>Android</li>
<li>Rester indépendant de la taille</li>
<li>Rester indépendant de la densité</li>
<li>Le répertoire de ressources <code class="codecolorer text default"><span class="text">res/</span></code></li>
<li>Le fichier AndroidManifest.xml</li>
<li>Images 9-Patch</li>
</ul>
</li>
<li><a href="#capteur">Détecter la présence des capteurs</a></li>
</ul>
<p>Rendre son application la plus généraliste possible a un coût. A garder dans un coin de sa tête pour chiffrer un projet <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" /></p>
<p><span id="more-6"></span><br />
</p>
<h1 id="os"></h1>
<p><strong>> Gérer les versions d&rsquo;Android</strong></p>
<p>Si au début de l&rsquo;année nous pouvions nous inquiéter de la disparité des versions de l&rsquo;OS, les choses semblent évoluer (à la vitesse d&rsquo;un escargot) dans le bon sens. La migration vers la 2.1 se généralise chez la plus part des opérateurs et constructeurs de téléphones comme en atteste <a href="http://android-france.fr/2009/12/05/et-les-smartphones-htc-qui-vont-etre-mis-a-jour-en-android-2-1-sont">Android-France</a> et ce&#8230; pour la grande joie des développeurs.</p>
<p>Cependant, il reste naturel de vouloir imposer une version à l&rsquo;utilisateur de la même façon que Google le fait avec <em>Google Earth</em>, <em>Google Maps Navigation</em>, <em>Google Search Gesture</em> et Android 2.0.1.</p>
<p>Dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code>, il suffit d&rsquo;ajouter la balise <code class="codecolorer text default"><span class="text">&lt;uses-sdk&gt;</span></code> (qui est automatiquement ajouté lors de la création d&rsquo;un projet sous Eclipse) :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;uses-sdk android:minSdkVersion=&quot;integer&quot; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:targetSdkVersion=&quot;integer&quot; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:maxSdkVersion=&quot;integer&quot; /&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Seul l&rsquo;attribut <code class="codecolorer text default"><span class="text">android:minSdkVersion</span></code> a une réelle importance. Il indique la version minimum du SDK nécessaire pour le fonctionnement de l&rsquo;application. S&rsquo;il n&rsquo;est pas renseigné, Android considère que l&rsquo;application est compatible avec toutes ses différentes versions. <code class="codecolorer text default"><span class="text">android:targetSdkVersion</span></code>, couplé à <code class="codecolorer text default"><span class="text">android:minSdkVersion</span></code>, indique que l&rsquo;application a été testée sur une version précise mais reste compatible avec les versions précédentes jusqu&rsquo;à <code class="codecolorer text default"><span class="text">minSdkVersion</span></code>. L&rsquo;unique but est d&rsquo;indiquer au système qu&rsquo;il n&rsquo;est pas nécessaire d&rsquo;assurer une compatibilité ascendante au runtime quand l&rsquo;application s&rsquo;exécute sur cette version. L&rsquo;intérêt est peut être de libérer des ressources mais cela reste à vérifier. Quant à <code class="codecolorer text default"><span class="text">android:maxSdkVersion</span></code>, l&rsquo;attribut n&rsquo;est pas recommandé et a perdu tout son sens depuis que la rétro compatibilité est assurée.</p>
<p>Ainsi pour imposer la version 2.1 :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt; &nbsp;<br />
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; &nbsp;<br />
&nbsp; package=&quot;com.android23.app&quot;&gt; &nbsp;<br />
&nbsp; ... &nbsp;<br />
&nbsp; &lt;uses-sdk android:minSdkVersion=&quot;7&quot; /&gt; &nbsp;<br />
&lt;/manifest&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Les versions sont numérotés de cette façon :<br />
<strong>7</strong> -> Android 2.1<br />
<strong>6</strong> -> Android 2.0.1<br />
<strong>5</strong> -> Android 2.0<br />
<strong>4</strong> -> Android 1.6<br />
<strong>3</strong> -> Android 1.5<br />
<strong>2</strong> -> Android 1.1<br />
<strong>1</strong> -> Android 1.0</p>
<p>La compatibilité <a href="http://developer.android.com/guide/appendix/api-levels.html#fc">ascendante</a> des versions du SDK est normalement assurée. Votre application développée en 2.0.1 fonctionnera sur un téléphone équipé d&rsquo;Android 2.1. Mais Google conseille tout de même de vérifier les changelogs afin de s&rsquo;assurer que les méthodes utilisées n&rsquo;ont pas été supprimées d&rsquo;une version à l&rsquo;autre.<br />
Concernant la compatibilité <a href="http://developer.android.com/guide/appendix/api-levels.html#bc">descendante</a>, elle n&rsquo;est évidement pas assurée. Google prend tout de même le soin de nous donner quelques pistes pour l&rsquo;assurer manuellement: <a href="http://developer.android.com/resources/articles/backward-compatibility.html">http://developer.android.com/resources/articles/backward-compatibility.html</a></p>
<p>Quant aux ressources (images, layout&#8230; du répertoire <code class="codecolorer text default"><span class="text">res/</span></code>), elles peuvent être spécifiques à une version du système. Je pense par exemple aux icones du Launcher qui ont subit un petit ravalement 3D en passant à la version 2.0. Pour cela vous pouvez ajouter au répertoire <code class="codecolorer text default"><span class="text">drawable</span></code> : <code class="codecolorer text default"><span class="text">-v&lt;api-level&gt;</span></code> et spécifier la version cible. Exemple: <code class="codecolorer text default"><span class="text">drawable-v6/</span></code> désigne des ressources qui ne seront chargées uniquement sur Android 2.0.1.</p>
<p>Enfin, certains mobiles bien qu&rsquo;en version 2.1 ne supporterons pas le flash 10.1 pour des problèmes de performance : <a href="http://android-france.fr/2010/02/26/adobe-flash/">http://android-france.fr/2010/02/26/adobe-flash/</a>. Pensez y aussi si vous souhaitiez réaliser une application en Flash à destination des mobiles Android.</p>
<p><ins>[ Mise à jour : 02/04/2010 ]</ins><br />
Une simplification de la gestion des versions d&rsquo;Android est en marche <a href="http://www.journaldugeek.com/2010/04/02/vers-une-simplification-dandroid/">http://www.journaldugeek.com/2010/04/02/vers-une-simplification-dandroid/</a></p>
<p><ins>[ Mise à jour : 22/06/2010 ]</ins><br />
A la mi-Juin 2010, <a href="http://developer.android.com/intl/fr/resources/dashboard/platform-versions.html">Google publie de nouvelles statistiques</a> :<br />
<img src="http://blog.developpez.com/media/stats-versions.png"/><br />
On apprend que la version 2.1 est installée sur plus de 50% des smart-phones se connectant à l&rsquo;AndroidMarket. Les versions 1.5, et 1.6 sont installées de manière équitable : 24.6% pour la première et 25.0% pour la seconde. Il est donc encore trop tôt pour les oublier. Quant aux versions 1.1,2.00 et 2.01, elles ne méritent plus l&rsquo;attention des développeurs.<br />
Avec la mise à jour du <a href="http://www.frandroid.com/21936/htc-hero-android-2-1-a-partir-du-29-juin-en-france/">HTC-Hero en 2.1</a> à la fin du mois, on peut s&rsquo;attendre à un fort déclin de la version 1.5.<br />
Le site <a href="http://android-france.fr/2010/06/11/le-point-sur-les-versions-android/">Android France</a> fait l&rsquo;état des versions Android installées sur les téléphones disponible en France.<br />
</p>
<h1 id="ecrans"></h1>
<p><strong>> Supporter les différents écrans</strong></p>
<p>La <a href="http://developer.android.com/guide/practices/screens_support.html">documentation Google</a> sur ce sujet est très complète, c&rsquo;est pourquoi je vous invite vivement à la lire bien qu&rsquo;elle soit en anglais. </p>
<p>Néanmoins j&rsquo;ai décidé de partager avec vous quelques points qui me paraissent essentiels. Je conseillerai également de jeter un oeil à l&rsquo;application démo <a href="http://developer.android.com/resources/samples/MultiResolution/index.html">MultiResolution</a> qui illustre le support des différents écrans et le mode paysage.<br />
</p>
<p>    <strong>>> Le support d&rsquo;Android</strong></p>
<p>Android répartit les différentes tailles et densités d&rsquo;écran de la façon suivante :</p>
<p><img src="http://blog.developpez.com/media/resolution-android.PNG"/></p>
<p>L&rsquo;écran et la densité par défaut est en jaune.</p>
<p>A titre indicatif, le <a href="http://android-france.fr/nexus-one">Google Nexus One</a>, <a href="http://android-france.fr/htc-desire/">HTC Desire</a>, <a href="http://android-france.fr/sony-ericsson-xperia-x10/">Sony Ericsson Xperia 10X</a> et <a href="http://android-france.fr/motorola-milestone/">Motorola Droid (milestone)</a> sont à ranger dans les écrans normaux à forte densité (<code class="codecolorer text default"><span class="text">normal-hdpi</span></code>). Les HTC <a href="http://android-france.fr/htc/htc-magic/">Magic</a>, <a href="http://android-france.fr/htc/htc-dream/">Dream</a>, <a href="http://android-france.fr/htc/htc-hero">Hero</a>, <a href="http://android-france.fr/htc-legend">Legend</a>, le <a href="http://android-france.fr/motorola-dext/">Motorola Dext</a> et le <a href="http://android-france.fr/samsung/samsung-galaxy/">Samsung Galaxy</a> sont à ranger dans les écrans normaux à densité moyenne (<code class="codecolorer text default"><span class="text">normal-mdpi</span></code>). Enfin le HTC <a href="http://android-france.fr/htc/htc-tattoo/">Tattoo</a>, le prochain HTC Halo, et les dérivés &laquo;&nbsp;Mini&nbsp;&raquo; des constructeurs sont à ranger dans les petits écrans à faible densité (<code class="codecolorer text default"><span class="text">small-ldpi</span></code>). Quant à la catégorie des écrans larges, elle concerne les tablettes PC.</p>
<p>Vous avez mal à la tête ? moi aussi&#8230; :'(</p>
<p>Vous devrez <a href="http://developer.android.com/guide/practices/screens_support.html#testing">tester votre application</a> au minimum sur 4 types d&rsquo;écrans (encerclés en rouge) pour cibler le maximum de téléphones. En créant différents AVD vous pourrez vous rendre compte des modifications à apporter rapidement :</p>
<p><img src="http://blog.developpez.com/media/avd-multiresolution.PNG"/></p>
<p>Pour gérer cette multitude d&rsquo;écrans, Android se base sur la configuration de son fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> (<code class="codecolorer text default"><span class="text">&lt;supports-screens&gt;</span></code>) mais également sur le nom des répertoires de ressources (répertoire <code class="codecolorer text default"><span class="text">res/</span></code>). Il est possible de leur rajouter des qualificatifs :</p>
<ul>
<li>pour les différentes taille des écrans : <ins>-large</ins>, <ins>-normal</ins>, et <ins>-small</ins></li>
<li>pour les différentes densités : <ins>-hdpi</ins>, <ins>-mdpi</ins>, et <ins>-ldpi</ins></li>
</ul>
<p>Si l&rsquo;application ne contient pas les ressources nécessaires pour les différentes résolutions et densités, le système adaptera les ressources présentes dans <code class="codecolorer text default"><span class="text">res/drawable/</span></code>, <code class="codecolorer text default"><span class="text">res/layout</span></code>&#8230; aux caractéristiques de l&rsquo;écran. Le résultat peut être des images moins net, des boutons plus grands&#8230;<br />
<br />
<ins>[ Mise à jour : 11/06/2010 ]</ins><br />
Google publie des statistiques concernant les écrans utilisés par les utilisateurs de l&rsquo;Android Market. Nous remarquons que les écrans <ins>mdpi</ins> et <ins>hdpi</ins> sont les plus répandus :<br />
<img src="http://blog.developpez.com/media/stats-screens-density.png"/></p>
<p>
    <strong>>> Rester indépendant de la taille de l&rsquo;écran</strong></p>
<p>Le problème est un peu similaire aux applications de bureau dont l&rsquo;interface doit s&rsquo;adapter à la dimension de la fenêtre. </p>
<p>Lors de la construction des écrans, évitez l&rsquo;emploi des <a href="http://developer.android.com/reference/android/widget/AbsoluteLayout.html">AbsolutLayout</a> qui impose de fixer les coordonnées X et Y de vos Button, TextView&#8230; qu&rsquo;ils contiennent. Privilégiez plutôt les <a href="http://developer.android.com/guide/topics/ui/layout-objects.html#relativelayout">RelativeLayout</a> qui permettent de placer vos éléments de façon relative au layout parent. Quand vous définissez les attributs <code class="codecolorer text default"><span class="text">layout_width</span></code> and <code class="codecolorer text default"><span class="text">layout_height</span></code> de vos Views, utilisez <code class="codecolorer text default"><span class="text">wrap_content</span></code>, ou <code class="codecolorer text default"><span class="text">fill_parent</span></code>.</p>
<p>Enfin pensez aux <a href="#nine">images 9-patch</a>. Elle vous faciliterons également la vie lors du passage en mode paysage de votre application.<br />
</p>
<p>    <strong>>> Rester indépendant de la densité de l&rsquo;écran</strong></p>
<p>La densité est basée naturellement sur la résolution de l&rsquo;écran mais aussi sur sa taille physique. Un écran de type <code class="codecolorer text default"><span class="text">normal-ldpi</span></code> contient moins de pixel sur 1cm² qu&rsquo;un écran <code class="codecolorer text default"><span class="text">normal-hdpi</span></code>. C&rsquo;est pourquoi un bouton dont la taille est définie en pixels apparaitra plus grand sur un écran de faible densité que sur un écran normal par exemple.</p>
<p>Pour éviter ce genre de désagrément, privilégiez l&rsquo;utilisation des <ins>dip</ins> ou <ins>dp</ins> au <ins>px</ins> lorsque vous spécifiez les tailles des boutons, des champs textes, des labels&#8230; Utilisez <ins>sp</ins> si vous spécifiez la taille du texte dans les <code class="codecolorer text default"><span class="text">EditText</span></code> par exemple. </p>
<p>Les éléments garderont ainsi la même taille quelque soit l&rsquo;écran.<br />
</p>
<p>    <strong>>> Le répertoire de ressources</strong></p>
<p>Lors de la création d&rsquo;un projet sous Eclipse, plusieurs répertoires par défaut sont créés. Il est possible de leur rajouter des suffixes qui indiqueront à Android quelles ressources charger selon l&rsquo;écran ou la densité :</p>
<ul>
<li>La taille
<ul>
<li><ins>-small</ins> : ressources dédiées aux écrans QVGA à basse densité</li>
<li><ins>-normal</ins> : ressources dédiés aux écrans par défaut, tel que le T-Mobile G1 &#8211; HTC Magic ou équivalent</li>
<li><ins>-large</ins> :  ressources dédiées aux écrans larges</li>
</ul>
</li>
<li>La densité
<ul>
<li><ins>-ldpi</ins> : ressources pour les écrans à faible densité (de 100 à 140 dpi)</li>
<li><ins>-mdpi</ins> : ressources pour les écrans de densité standart (de 140 à 180 dpi)</li>
<li><ins>-hdpi</ins> : ressources pour les écrans à forte densité (de 190 à 250 dpi)</li>
<li><ins>-nodpi</ins> : ressources indépendante de la densité de l&rsquo;écran</li>
</ul>
</li>
</ul>
<p>Quelques exemples :<br />
<code class="codecolorer text default"><span class="text">res/drawable/</span></code>     <em>images communes à tous les types d&rsquo;écrans</em><br />
<code class="codecolorer text default"><span class="text">res/drawable-hdpi/</span></code>     <em>images pour les écrans à forte densité</em><br />
<code class="codecolorer text default"><span class="text">res/drawable-ldpi/</span></code>     <em>images pour les écrans à faible densité</em><br />
<code class="codecolorer text default"><span class="text">res/drawable-mdpi/</span></code>     <em>images pour des écrans à densité normal</em><br />
<code class="codecolorer text default"><span class="text">res/layout/my_layout.xml</span></code>     <em>layout pour les écrans normaux</em><br />
<code class="codecolorer text default"><span class="text">res/layout-land/my_layout.xml</span></code>     <em>layout pour les écrans normaux en mode paysage</em><br />
<code class="codecolorer text default"><span class="text">res/layout-small/my_layout.xml</span></code>     <em>layout pour les petits écrans</em><br />
<code class="codecolorer text default"><span class="text">res/layout-large/my_layout.xml</span></code>     <em>layout pour les écrans larges</em><br />
<code class="codecolorer text default"><span class="text">res/layout-large-land/my_layout.xml</span></code>     <em>pour les écrans larges en mode paysage</em></p>
<p>Définir <code class="codecolorer text default"><span class="text">drawable/</span></code>, <code class="codecolorer text default"><span class="text">drawable-hdpi/</span></code>, <code class="codecolorer text default"><span class="text">drawable-ldpi/</span></code>, <code class="codecolorer text default"><span class="text">drawable-mdpi/</span></code>, <code class="codecolorer text default"><span class="text">layout/</span></code> et <code class="codecolorer text default"><span class="text">layout-land/</span></code> suffit amplement. D&rsquo;ailleurs sous Eclipse, les répertoires <code class="codecolorer text default"><span class="text">drawable-hdpi/</span></code>, <code class="codecolorer text default"><span class="text">drawable-ldpi/</span></code>, <code class="codecolorer text default"><span class="text">drawable-mdpi/</span></code>, <code class="codecolorer text default"><span class="text">layout/</span></code> sont par défaut créés à l&rsquo;initialisation d&rsquo;un nouveau projet.</p>
<p>Si Android ne trouve pas de ressources spécifiques à la configuration du matériel, il chargera les ressources présentes dans les répertoires sans suffixes tel <code class="codecolorer text default"><span class="text">res/drawable</span></code>, <code class="codecolorer text default"><span class="text">res/layout</span></code>&#8230;<br />
</p>
<p>    <strong>>> Le fichier AndroidManifest.xml</strong></p>
<p>Vous pouvez spécifier dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> les écrans supportés avec la balise <code class="codecolorer text default"><span class="text">&lt;supports-screens&gt;</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt; <br />
&nbsp; &lt;supports-screens <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:largeScreens=&quot;true&quot; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:normalScreens=&quot;true&quot; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:smallScreens=&quot;true&quot; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; android:anyDensity=&quot;true&quot; /&gt; <br />
&lt;/manifest&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p><code class="codecolorer text default"><span class="text">android:largeScreens</span></code>, <code class="codecolorer text default"><span class="text">android:normalScreens</span></code>, et <code class="codecolorer text default"><span class="text">android:smallScreens</span></code> indiquent si les écrans sont supportés (<ins>true</ins>) ou non (<ins>false</ins>).</p>
<p>La valeur <ins>false</ins> n&rsquo;empêche pas l&rsquo;installation de l&rsquo;application. Android simulera un écran de type <code class="codecolorer text default"><span class="text">normal-mdpi</span></code> sur les téléphones non supportés. Sur <a href="http://developer.android.com/guide/practices/screens_support.html#compatibility-examples">des écrans larges</a>, l&rsquo;application sera entourée de noir, tant dis que sur des écrans petits, l&rsquo;application sera coupée.</p>
<p>L&rsquo;attribut <code class="codecolorer text default"><span class="text">android:anyDensity</span></code> indique si Android met à l&rsquo;échelle les valeurs en pixel ou non. Ainsi à <ins>true</ins>, l&rsquo;OS laisse l&rsquo;application s&rsquo;assurer de sa compatibilité. Votre bouton apparaitra plus gros sur un écran <code class="codecolorer text default"><span class="text">large</span></code> ou plus petit sur un écran <code class="codecolorer text default"><span class="text">small</span></code> si vous lui avez spécifié des valeurs en <ins>px</ins>. A <ins>false</ins>, l&rsquo;OS simule sur le téléphone un écran <code class="codecolorer text default"><span class="text">medium-mdpi</span></code> et adapte les valeurs <ins>px</ins>.</p>
<p>Quelques soit la version de l&rsquo;OS supportée, <code class="codecolorer text default"><span class="text">android:normalScreens</span></code> est par défaut à <ins>true</ins>. Pour le reste, tout dépend de la version d&rsquo;Android :</p>
<ul>
<li>Si l&rsquo;application vise Android 1.5 et ses versions inférieures, tous les autres attributs sont à <ins>false</ins>.
</li>
<li>Si l&rsquo;application vise Android 1.6 et ses versions supérieures, tous les autres attributs sont à <ins>true</ins>.</li>
</ul>
<p>Puisque la version 2.1 se généralise, votre application est par défaut compatible avec tous les écrans. Il ne vous reste plus qu&rsquo;à assurer la compatibilité des différentes densités en employant des <ins>dip</ins> et en proposant des ressources destinées aux écrans <code class="codecolorer text default"><span class="text">ldpi</span></code>,<code class="codecolorer text default"><span class="text">mdpi</span></code> et <code class="codecolorer text default"><span class="text">hdpi</span></code>.<br />
</p>
<p>    <strong>>> Images 9-Patch</strong></p>
<p>Les images 9-Patch (<a href="http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch">NinePatchDrawable</a>) sont des images au format <a href="http://fr.wikipedia.org/wiki/Portable_Network_Graphics">PNG</a> dont nous avons spécifié les zones étirables et les zones statiques. Elles doivent être enregistrées avec l&rsquo;extension <code class="codecolorer text default"><span class="text">.9.png</span></code>.</p>
<p>A l&rsquo;aide de 2 traits noirs épais d&rsquo;1 pixel, un en haut et un à gauche de l&rsquo;image, nous définissons une grille de 9 cases :</p>
<p><img src="http://blog.developpez.com/media/nine-patch-sample.png"/></p>
<p>Ces deux traits noirs seront seulement interprétés par l&rsquo;OS et n&rsquo;apparaitront pas à l&rsquo;écran.  Ainsi, seules les cases présentes dans les zones vertes et mauves seront redimensionnées tant dis que les autres resteront fixes si l&rsquo;image est étirée en hauteur ou en largeur :</p>
<p><img src="http://blog.developpez.com/media/nine-patch-zoom.png" /></p>
<p>Il est possible de spécifier une zone de <em>padding</em> avec un trait sur le bord droit et un en bas de l&rsquo;image. Cette zone est optionnelle mais permet de définir l&rsquo;endroit où Android positionnera les éléments à l&rsquo;intérieur de la vue.</p>
<p>Les 9-Patch trouvent toutes leur utilité dans la création de Button ou de TextView dont le fond s&rsquo;adapte à la longueur du texte mais également lors du passage en mode paysage de votre application.</p>
<p>Pour finir, Google nous propose un outil pour éditer les zones étirables et de contenu: <code class="codecolorer text default"><span class="text">draw9patch.bat</span></code> qui se trouve dans le répertoire <code class="codecolorer text default"><span class="text">tools/</span></code> du SDK. Pour ouvrir un fichier <code class="codecolorer text default"><span class="text">.png</span></code> ou <code class="codecolorer text default"><span class="text">.9.png</span></code>, il suffit d&rsquo;aller dans <em>File > Open 9-patch</em> :</p>
<p><img src="http://blog.developpez.com/media/draw-9-patch.PNG" /></p>
<p>Sur la panneau de droite, vous verrez le comportement de votre image  dans 3 situations :
<ul>
<li>étirement horizontal</li>
<li>étirement vertical </li>
<li>étirement horizontal et vertical</li>
</ul>
<p>En cochant la case <em>Show patches</em>, dans la barre d&rsquo;outils du bas, vous verrez les zones étirables de votre image sur le panneau de gauche. En cochant la case <em>Show content</em> vous verrez en bleu , dans le panneau droit, le padding défini.</p>
<p></p>
<h1 id="capteur"></h1>
<p><strong>> Détecter la présence des capteurs</strong></p>
<p>Tous les capteurs ne sont pas présents dans les téléphones. Ainsi le <a href="http://android-france.fr/htc-hero/">HTC Hero</a> et le <a href="http://android-france.fr/htc/htc-tattoo/">Tatto</a> ne possèdent que l&rsquo;accéléromètre et une boussole digitale alors que le <a href="http://android-france.fr/nexus-one">Nexus One</a> est complet. Avant de les utiliser il est préférable de les détecter :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); <br />
&nbsp;<br />
List&lt;Sensor&gt; sensorsList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER); <br />
if(sensorsList.size()&gt;0) { <br />
&nbsp; Log.d(&quot;SensorManager&quot;,&quot;L'accéléromètre est présent&quot;); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Vous pouvez remplacer <code class="codecolorer text default"><span class="text">Sensor.TYPE_ACCELEROMETER</span></code> par :</p>
<ul>
<li>TYPE_ALL (pour tous les lister)</li>
<li><del>TYPE_GYROSCOPE</del></li>
<li>TYPE_LIGHT</li>
<li>TYPE_MAGNETIC_FIELD</li>
<li>TYPE_ORIENTATION</li>
<li><del>TYPE_PRESSURE</del></li>
<li>TYPE_PROXIMITY</li>
<li><del>TYPE_TEMPERATURE</del></li>
</ul>
<p>Ceux barrés n&rsquo;apparaissent sur aucun téléphone.</p>
<p></p>
<p><strong>> Conclusion</strong></p>
<p>Il est nécessaire de garder cette problématique en tête au démarrage d&rsquo;un projet. Les solutions sont simples et les tests rapides. Il serait dommage de se priver de nombreux utilisateurs ;).</p>
<p><em>Portions of this page are reproduced from work created and shared by the Android Open Source Project  and used according to terms described in the Creative Commons 2.5 Attribution License.</em> </p>
<p></p>
<p><strong>> Ressources</strong></p>
<ul>
<li>
<p>http://developer.android.com/guide/topics/manifest/uses-sdk-element.html</li>
<li>
<p>http://developer.android.com/resources/samples/MultiResolution/index.html</li>
<li>
<p>http://developer.android.com/guide/appendix/api-levels.html</p>
</li>
<li>
<p>http://www.brighthub.com/mobile/google-android/articles/61895.aspx</p>
</li>
<li>
<p>http://www.higherpass.com/Android/Tutorials/Android-9-Patch-Scaled-Png-Image-Guide/</p>
</li>
<li><a href="http://www.developpez.net/forums/d834262/club-professionnels-informatique/actualites/fragmentation-dandroid-probleme-developpeurs-final-oui-repond-google/">http://www.developpez.net/forums/d834262/club-professionnels-informatique/actualites/fragmentation-dandroid-probleme-developpeurs-final-oui-repond-google/</a>
</li>
</ul>
<p>
<strong>> Content License</strong><br />
Portions of this page are reproduced from work created and shared by the Android Open Source Project  and used according to terms described in the Creative Commons 2.5 Attribution License. </p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Développer sur Android derrière un proxy avec l’émulateur</title>
		<link>https://blog.developpez.com/ndruet/p8495/android/developper_sur_android_derriere_un_proxy</link>
		<comments>https://blog.developpez.com/ndruet/p8495/android/developper_sur_android_derriere_un_proxy#comments</comments>
		<pubDate>Fri, 15 Jan 2010 14:33:25 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[C’est un peu la croix et la bannière de développer sous Eclipse derrière un proxy nécessitant une authentification avec l’émulateur d’Android. Nous allons voir ce qui est possible et ce qui ne l&#8217;est pas : Installation du SDK Spécifier le proxy à l&#8217;émulateur Se connecter à un serveur distant via HttpURLConnection Se connecter à un serveur distant via DefaultHttpClient > Installation de l’environnement : windows, Eclipse 3.5, ADT 0.9.7 et le SDK r6 [edit du [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>C’est un peu la croix et la bannière de développer sous Eclipse derrière un proxy nécessitant une authentification avec l’émulateur d’Android. Nous allons voir ce qui est possible et ce qui ne l&rsquo;est pas : </p>
<ul>
<li><a href="#sdk">Installation du SDK</a></li>
<li><a href="#proxy">Spécifier le proxy à l&rsquo;émulateur</a></li>
<li><a href="#httpurlconnection">Se connecter à un serveur distant via <em>HttpURLConnection</em></a></li>
<li><a href="#defaulthttpclient">Se connecter à un serveur distant via <em>DefaultHttpClient</em></a></li>
</ul>
<p><span id="more-3"></span><br />
</p>
<h1 id="sdk"></h1>
<p><strong>> Installation de l’environnement : windows, Eclipse 3.5, ADT 0.9.7 et le SDK r6</strong></p>
<p><ins>[edit du 11/06/2010 ]</ins><br />
Si les précédents SDK pouvaient être installés directement sans nécessiter une connexion internet, Google a décidé depuis Décembre de changer sa façon de faire. L’archive du SDK ne contient que le répertoire <em>sdk/tools</em>. </p>
<p>La plateforme Android (1.5, 1.6, 2.1 et 2.2), la documentation, les drivers USB se téléchargent via l&rsquo;installateur (<em>sdk/SDK Setup.exe</em>). C&rsquo;est une vision plus modulaire et plus pratique pour eux.</p>
<p>Allez dans <em>Settings </em>et saisissez l’adresse et le port du proxy. Cochez éventuellement <em>Force Https</em>.</p>
<p><img src="http://blog.developpez.com/media/install-sdk2.png" alt="install-sdk2" /></p>
<p>Mais si comme moi votre Proxy nécessite une identification, et bien l’erreur 407 sera la seule donnée que vous récupérerez.</p>
<p>Deux solutions existent:</p>
<ul>
<li>La plus simple est de télécharger les composants du SDK sur une machine ayant un accès direct à internet puis de les copier/coller sur votre machine de développement.</li>
<li>La seconde consiste à installer SQUID (multi-plateforme), un proxy à installer en local sur votre machine qui s&rsquo;occupera d&rsquo;effectuer l&rsquo;authentification auprès du proxy de votre entreprise. Malheureusement je n&rsquo;ai pas essayé. Mais J.S décrit la marche à suivre sur son blog : <a href="http://sastriawan.blogspot.com/2009/10/workaround-on-installing-android-sdk-20.html">http://sastriawan.blogspot.com/2009/10/workaround-on-installing-android-sdk-20.html</a> et sur le forum <a href="http://androidforums.com/android-developers/21088-problem-sdk-installation.html">http://androidforums.com/android-developers/21088-problem-sdk-installation.html</a> un post détaille comment faire sous Windows</li>
</ul>
<p></p>
<h1 id="proxy"></h1>
<p><strong>> Spécifier le proxy à l&rsquo;émulateur</strong></p>
<p><ins>[edit du 11/06/2010 ]</ins><br />
<em>-http-proxy</em> est une option au lancement de l’émulateur. Je n&rsquo;ai jamais réussi à faire fonctionner le navigateur internet et GoogleMap avec.</p>
<p>Du temps du SDK 1.6, il était possible d&rsquo;utiliser le navigateur en définissant l&rsquo;adresse du proxy et son port en éditant un APN : <em>Home > Menu > Settings > Wireless Controls > Mobile networks > Access Point Names</em></p>
<p>Depuis le SKD d&rsquo;Android 2.0.1, 2.1 et la récente 2.2, plus rien de marche :<br />
<a href="http://code.google.com/p/android/issues/detail?id=5508">http://code.google.com/p/android/issues/detail?id=5508</a></p>
<p>C&rsquo;est tout même absurde qu&rsquo;on ne puisse pas développer correctement derrière un proxy d&rsquo;entreprise.</p>
<p></p>
<h1 id="httpurlconnection"></h1>
<p><strong>> Se connecter à un serveur distant via HttpURLConnection</strong></p>
<p>Vous pouvez récupérer les préférences de l’utilisateur :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">final String proxyHost = android.net.Proxy.getDefaultHost(); <br />
final int proxyPort = android.net.Proxy.getDefaultPort();</div></td></tr></tbody></table></div>
</blockquote>
<p>Puis&#8230;</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">String url = &quot;http://www.google.com/ig/api?weather=Nantes.FRANCE&quot;, username = &quot;username &quot;, password = &quot;password &quot;; <br />
&nbsp;<br />
Authenticator.setDefault(new SimpleAuthenticator(username,password)); <br />
URL server = new URL(url); <br />
&nbsp;<br />
Properties systemProperties = System.getProperties(); <br />
systemProperties.setProperty(&quot;http.proxyHost&quot;,proxyHost ); <br />
systemProperties.setProperty(&quot;http.proxyPort&quot;,proxyPort ); <br />
&nbsp;<br />
HttpURLConnection connection = (HttpURLConnection)server.openConnection(); <br />
connection.connect(); <br />
InputStream xmlGoogleWeather = connection.getInputStream() <br />
...</div></td></tr></tbody></table></div>
</blockquote>
<p>La classe <em>SimpleAuthenticator </em>:</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class SimpleAuthenticator extends Authenticator { <br />
&nbsp; private String username, password; <br />
&nbsp;<br />
&nbsp; public SimpleAuthenticator(String username, String password) { <br />
&nbsp; &nbsp; this.username = username; <br />
&nbsp; &nbsp; this.password = password; <br />
&nbsp; } <br />
&nbsp;<br />
&nbsp; protected PasswordAuthentication getPasswordAuthentication() { <br />
&nbsp; &nbsp; return new PasswordAuthentication(username, password.toCharArray()); <br />
&nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p></p>
<h1 id="defaulthttpclient"></h1>
<p><strong>> Se connecter à un serveur distant via DefaultHttpClient</strong></p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">final String proxyHost = android.net.Proxy.getDefaultHost(); <br />
final int proxyPort = android.net.Proxy.getDefaultPort(); <br />
&nbsp;<br />
DefaultHttpClient client = new DefaultHttpClient(); <br />
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, new HttpHost(proxyHost , proxyPort )); <br />
&nbsp;<br />
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(&quot;username:password&quot;); <br />
client.getCredentialsProvider().setCredentials(new AuthScope(proxyHost ,proxyPort ),credentials); <br />
&nbsp;<br />
HttpResponse response = client.execute(new HttpGet(&quot;http://...&quot;)); <br />
InputStream xmlGoogleWeather = response.getEntity().getContent(); <br />
...</div></td></tr></tbody></table></div>
</blockquote>
<p>
<strong>> Conclusion</strong></p>
<p>On ne peut pas dire que Google nous facilite la tâche. Travailler derrière un proxy n&rsquo;est pourtant pas si étonnant que cela en entreprise. Et cette histoire dure depuis les premières versions du SDK. </p>
<p>Je tiendrai à jour le post au fil de mes recherches&#8230;</p>
<p>
<strong>> Ressources</strong></p>
<ul>
<li><a href="http://stackoverflow.com/questions/28380/proxy-with-android-emulator">http://stackoverflow.com/questions/28380/proxy-with-android-emulator</a></li>
<li><a href="http://markmail.org/message/r2mpgv7dqaeti3zf">http://markmail.org/message/r2mpgv7dqaeti3zf</a></li>
<li><a href="http://www.coderanch.com/t/462906/Android/Mobile/connect-internet-behind-proxy">http://www.coderanch.com/t/462906/Android/Mobile/connect-internet-behind-proxy</a></li>
<li><a href="http://groups.google.com/group/android-developers/browse_thread/thread/1105da659b20a176">http://groups.google.com/group/android-developers/browse_thread/thread/1105da659b20a176</a></li>
<li><a href="http://www.developer.com/java/other/article.php/1551421/Questions-on-HttpURLConnection-and-Proxies.htm">http://www.developer.com/java/other/article.php/1551421/Questions-on-HttpURLConnection-and-Proxies.htm</a></li>
<li><a href="http://www.rgagnon.com/javadetails/java-0085.html">http://www.rgagnon.com/javadetails/java-0085.html</a></li>
<li><a href="http://sastriawan.blogspot.com/2009/10/workaround-on-installing-android-sdk-20.html">http://sastriawan.blogspot.com/2009/10/workaround-on-installing-android-sdk-20.html</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Simuler la boussole, l&#8217;accéléromètre, et les changements d&#8217;orientation sans téléphone</title>
		<link>https://blog.developpez.com/ndruet/p8740/android/simuler_la_boussole_l_accelerometre_et_l</link>
		<comments>https://blog.developpez.com/ndruet/p8740/android/simuler_la_boussole_l_accelerometre_et_l#comments</comments>
		<pubDate>Wed, 24 Mar 2010 16:51:19 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Durant le développement d&#8217;une application Android, il arrive parfois que le téléphone mobile utilisé pour les tests ne soit pas sous la main. C&#8217;est pourquoi Google fournit un émulateur avec le SDK. Malheureusement ses fonctions restent limitées. En effet il ne permet pas de simuler la boussole, les changements d&#8217;orientation ou bien encore les accélérations auxquelles le téléphone est soumis. C&#8217;est par hasard que je suis tombé sur le projet OpenIntents hébergé sur Google Code. [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Durant le développement d&rsquo;une application Android, il arrive parfois que le téléphone mobile utilisé pour les tests ne soit pas sous la main. C&rsquo;est pourquoi Google fournit un émulateur avec le SDK. Malheureusement ses fonctions restent limitées. En effet il ne permet pas de simuler la boussole, les changements d&rsquo;orientation ou bien encore les accélérations auxquelles le téléphone est soumis. C&rsquo;est par hasard que je suis tombé sur le projet <a href="http://code.google.com/p/openintents/">OpenIntents</a> hébergé sur Google Code. En plus de proposer des Intents open-sources, l&rsquo;équipe met à disposition des outils pour les développeurs dont <a href="http://code.google.com/p/openintents/wiki/SensorSimulator">Sensor Simulator</a>.</p>
<p><a href="http://code.google.com/p/openintents/wiki/SensorSimulator">Sensor Simulator</a> simule 4 capteurs : l&rsquo;accéléromètre, la boussole magnétique, les changements d&rsquo;orientations et le thermomètre (qui ne nous intéresse pas). Sont absents le détecteur de proximité et de luminosité. L&rsquo;outil se décompose en une application Java s&rsquo;exécutant sur le poste de travail et d&rsquo;une application Android tournant en tâche de fond qui simulera les stimulations des capteurs sur le téléphone.</p>
<p>Nous allons voir l&rsquo;<a href="#setup">installation</a>, <a href="#config">la configuration</a> et <a href="#update">les quelques modifications</a> à apporter à une application pour prendre en compte le simulateur.</p>
<p><span id="more-8"></span><br />
<br />
Un bon dessin valant mieux qu&rsquo;un long discours. Voici une illustration de son fonctionnement issue de la page du projet :</p>
<p><img src="http://openintents.googlecode.com/svn/images/screenshots/Release-0.1.2/sensorsimulator01a_anim.gif" height="245" width="360"/><br />
</p>
<h1 id="setup"></h1>
<p><strong>> Installation de SensorSimulator</strong></p>
<p>    <strong>>> Téléchargement</strong></p>
<p>A l&rsquo;heure où j&rsquo;écris ces lignes, la version actuelle est la <code class="codecolorer text default"><span class="text">1.0.0 beta1</span></code>.<br />
Lien pour le téléchargement : <a href="http://openintents.googlecode.com/files/sensorsimulator-1.0.0-beta1.zip">http://openintents.googlecode.com/files/sensorsimulator-1.0.0-beta1.zip</a>.</p>
<p>L&rsquo;archive est à décompresser dans le répertoire de votre choix. </p>
<p>Elle se présente ainsi :</p>
<ul>
<li><ins>bin/</ins> : l&rsquo;application pour le poste de travail <em>sensorsimulator.jar</em> et celle pour Android <em>SensorSimulatorSettings.apk</em></li>
<li><ins>lib/</ins> : <em>sensorsimulator-lib.jar</em>, la librairie à rajouter à votre application le temps des tests</li>
<li><ins>release/</ins> : fichier Ant pour reconstruire <em>sensorsimulator.jar</em></li>
<li><ins>samples/</ins> : l&rsquo;application de démo</li>
<li><ins>SensorSimulator/</ins> : les sources de l&rsquo;application pour le poste de travail</li>
<li><ins>SensorSimlatorSetting/</ins> : les sources de l&rsquo;application Android</li>
</ul>
<p>
    <strong>>> Installation sur le poste de travail</strong></p>
<p>Aucune installation n&rsquo;est nécessaire. Il suffit simplement de lancer le fichier jar <em>sensorsimulator.jar</em> du répertoire <ins>bin/</ins> pour que l&rsquo;application démarre et apparaisse à l&rsquo;écran.</p>
<p>Une applet est également disponible à cette adresse : http://www.openintents.org/en/node/6<br />
</p>
<p>    <strong>>> Installation sur le téléphone</strong></p>
<p>Il est nécessaire d&rsquo;installer <em>SensorSimulatorSettings.apk</em> sur l&rsquo;émulateur. Deux manières s&rsquo;offrent à nous :</p>
<ul>
<li>En ligne de commande, dans le répertoire <ins>bin/</ins>, une fois l&rsquo;émulateur démarré :<br />
<blockquote><code class="codecolorer text default"><span class="text">adb -install SensorSimulatorSettings.apk</span></code></p></blockquote>
</li>
<li>Sous Eclipse, en créant un projet Android, puis en sélectionnant les sources du répertoire <ins>SensorSimlatorSetting/</ins>. Vous devrez indiquer une version minimum du SDK et l&rsquo;ajouter dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> (<code class="codecolorer text default"><span class="text">&lt;uses-sdk android:minSdkVersion=&quot;7&quot;&gt;</span></code>)</li>
</ul>
<p>L&rsquo;application <code class="codecolorer text default"><span class="text">SensorSimulatorSettings</span></code> doit tourner en tâche de fond pendant les tests.<br />
</p>
<h1 id="config"></h1>
<p><strong>> Configuration</strong></p>
<p>Il ne vous reste plus qu&rsquo;à configurer les deux applications pour qu&rsquo;elles communiquent entre elles.</p>
<ul>
<li>Sur votre poste de travail, lancer en premier <code class="codecolorer text default"><span class="text">SensorSimulator.jar</span></code>. Le port sur lequel se fera la communication peut être changé. Vous pouvez laisser le numéro par défaut 8010 :
<p><img src="http://blog.developpez.com/media/sensor-emulator.png"/>
</li>
<li>Sous Android, lancez l&rsquo;application <img src="http://blog.developpez.com/media/sensor-emulator-setting.png"/> <code class="codecolorer text default"><span class="text">SensorSimulatorSetting</span></code>. Dans l&rsquo;onglet <strong>Setting</strong>, saisissez le numéro de port et l&rsquo;adresse IP <code class="codecolorer text default"><span class="text">10.0.2.2</span></code> pour indiquer la boucle locale de votre poste de travail à l&rsquo;émulateur (et non <code class="codecolorer text default"><span class="text">0.0.0.0</span></code> qui fait référence à l&rsquo;émulateur lui même)  :
<p><img src="http://blog.developpez.com/media/sensor-emulator-settings.png"/></p>
<p>Dans l&rsquo;onglet <strong>Testing</strong>, cliquez sur &laquo;&nbsp;connect&nbsp;&raquo; puis cochez les 3 capteurs :</p>
<p><img src="http://blog.developpez.com/media/sensor-emulator-testing.png" /></li>
</ul>
<p>Si tout se passe bien, en bougeant le pavé rouge de <code class="codecolorer text default"><span class="text">SensorSimulator.jar</span></code> sur votre poste de travail, les valeurs des capteurs devraient se mettre à jour sous Android.<br />
</p>
<h1 id="config"></h1>
<p><strong>> Modifications à apporter dans votre application</strong></p>
<p>Les modifications à apporter aux applications ne sont pas nombreuses.</p>
<p>Avant tout, il est nécessaire d&rsquo;ajouter <code class="codecolorer text default"><span class="text">sensorsimulator-lib.jar</span></code> à son projet. La librairie se trouve dans le répertoire <ins>lib/</ins> de l&rsquo;archive téléchargée.</p>
<p>SensorEmulator n&rsquo;ayant pas été porté sous Android 2.1, l&rsquo;Activity doit implémenter l&rsquo;interface dépréciée <a href="http://developer.android.com/intl/fr/reference/android/hardware/SensorListener.html">SensorListener</a> au lieu de <a href="http://developer.android.com/intl/fr/reference/android/hardware/SensorEventListener.html">SensorEventListener</a>.</p>
<p>La méthode <code class="codecolorer text default"><span class="text">onSensorChanged(int , float[] )</span></code> doit alors être définie :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public void onSensorChanged(int sensor, float[] values) { <br />
&nbsp; &nbsp; switch(sensor) { <br />
&nbsp; &nbsp; case SensorManager.SENSOR_ACCELEROMETER: <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;onSensorChanged&quot;,&quot;Accelerometer: &quot; + values[0] + &quot;, &quot; + values[1] + &quot;, &quot; + values[2]); <br />
&nbsp; &nbsp; break; <br />
&nbsp; &nbsp; case SensorManager.SENSOR_MAGNETIC_FIELD: <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;onSensorChanged&quot;,&quot;Compass: &quot; + values[0] + &quot;, &quot; + values[1] + &quot;, &quot; + values[2]); <br />
&nbsp; &nbsp; break; <br />
&nbsp; &nbsp; case SensorManager.SENSOR_ORIENTATION: <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;onSensorChanged&quot;,&quot;Orientation: &quot; + values[0] + &quot;, &quot; + values[1] + &quot;, &quot; + values[2]); <br />
&nbsp; &nbsp; break; <br />
&nbsp; &nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous déclarons un <code class="codecolorer text default"><span class="text">SensorManagerSimulator</span></code> dans l&rsquo;activity :</p>
<blockquote><p><code class="codecolorer text default"><span class="text">private SensorManagerSimulator sensorManager = null;</span></code></p></blockquote>
<p>&#8230; que nous initialisons dans la méthode <code class="codecolorer text default"><span class="text">onCreate()</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">sensorManager = SensorManagerSimulator.getSystemService(this, SENSOR_SERVICE); <br />
sensorManager.connectSimulator();</div></td></tr></tbody></table></div>
</blockquote>
<p>Pour terminer, il reste à définir la liste des capteurs en écoute dans la méthode <code class="codecolorer text default"><span class="text">onResume()</span></code>, de la même manière qu&rsquo;avec le <a href="http://developer.android.com/intl/fr/reference/android/hardware/SensorManager.html">SensorManager</a> du SDK :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Override <br />
protected void onResume() { <br />
&nbsp; &nbsp; super.onResume(); <br />
&nbsp; &nbsp; sensorManager.registerListener(this, SensorManager.SENSOR_ACCELEROMETER | <br />
&nbsp; &nbsp; &nbsp; &nbsp; SensorManager.SENSOR_MAGNETIC_FIELD | <br />
&nbsp; &nbsp; &nbsp; &nbsp; SensorManager.SENSOR_ORIENTATION, <br />
&nbsp; &nbsp; &nbsp; &nbsp; SensorManager.SENSOR_DELAY_FASTEST); <br />
} <br />
&nbsp;<br />
@Override <br />
protected void onStop() { <br />
&nbsp; &nbsp; &nbsp;sensorManager.unregisterListener(this); <br />
&nbsp; &nbsp; &nbsp;super.onStop(); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>The end <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /><br />
</p>
<h1 id="config"></h1>
<p><strong>> Ressources</strong></p>
<ul>
<li><a href="http://www.frandroid.com/254/openintents-des-standards-pour-les-intents-dandroid">http://www.frandroid.com/254/openintents-des-standards-pour-les-intents-dandroid</a></li>
<li><a href="http://www.openintents.org/en/">http://www.openintents.org/en/</a></li>
<li><a href="http://code.google.com/p/openintents/">http://code.google.com/p/openintents/</a></li>
<li><a href="http://code.google.com/p/openintents/wiki/SensorSimulator">http://code.google.com/p/openintents/wiki/SensorSimulator</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Utiliser les services sous Android</title>
		<link>https://blog.developpez.com/ndruet/p8571/android/creation_de_service</link>
		<comments>https://blog.developpez.com/ndruet/p8571/android/creation_de_service#comments</comments>
		<pubDate>Fri, 26 Feb 2010 13:54:06 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[A l&#8217;instar des Activities, des Intents, les services font partie des briques essentielles d&#8217;Android. Ils ne disposent pas d&#8217;interface utilisateur mais fonctionnent en arrière plan pour une période de temps indéfinie. L&#8217;exemple le plus courant est le lecteur de musique, qui vous permet d&#8217;écouter vos mp3 sans bloquer la navigation sur internet ou consultre la liste des vos contacts. Un service peut également rapatrier des données sur internet tels que des flux RSS. Dans ce [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>A l&rsquo;instar des Activities, des Intents, les services font partie des briques essentielles d&rsquo;Android. Ils ne disposent pas d&rsquo;interface utilisateur mais fonctionnent en arrière plan pour une période de temps indéfinie. L&rsquo;exemple le plus courant est le lecteur de musique, qui vous permet d&rsquo;écouter vos mp3 sans bloquer la navigation sur internet ou consultre la liste des vos contacts. Un service peut également rapatrier des données sur internet tels que des flux RSS.</p>
<p>Dans ce long article nous aborderons :</p>
<ul>
<li><a href="#services">Les services (threads, cycle de vie&#8230;)</a></li>
<li><a href="#localservice">Services Locaux (LocalService)</a>
<ul>
<li>Déclaration du service</li>
<li>Implémentation des listeners</li>
<li>Implémentation d&rsquo;un binder</li>
</ul>
</li>
<li><a href="#remoteservice">Services Distants (RemoteService)</a>
<ul>
<li>Déclaration du service</li>
<li>Mise en place des Callbacks</li>
<li>Démarrer le service au boot du téléphone</li>
<li>Exemple : Connexion au service MP3</li>
</ul>
</li>
</ul>
<p><span id="more-5"></span></p>
<h1 id="services"></h1>
<p><strong>> Les services (threads, cycle de vie&#8230;)</strong></p>
<p>Les services ont pour but de réaliser des tâches de fond sans aucune interaction avec l&rsquo;utilisateur pour une durée indéfinie. Il existe deux type de services :</p>
<ul>
<li>les services locaux (ou LocalService) qui s&rsquo;exécutent dans le même processus que votre application</li>
<li>Les services distants (ou RemoteService) qui s&rsquo;exécutent dans un processus différent de celui de application</li>
</ul>
<p>Les services s&rsquo;exécutent dans le Thread principal du processus parent. Ils doivent être déclarés dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code>: <code class="codecolorer text default"><span class="text">&lt;service android:name=&quot;.subpackagename.ServiceName&quot;/&gt;</span></code></p>
<p>Ils doivent étendre la classe <a href="http://developer.android.com/reference/android/app/Service.html">Service</a> dont vous devrez surcharger les méthodes suivantes en fonction de vos besoins :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void onCreate(); // initialisation des ressources <br />
void onStart(Intent intent); // SDK&lt;2.0 la tâche de fond démarre <br />
void onStartCommand(Intent intent, int flags, int startId); // SDK&gt;2.0 la tâche de fond démarre <br />
void onDestroy(); // libération des ressources <br />
&nbsp;<br />
&nbsp;<br />
IBinder onBind(Intent intent); // connexion client distant <br />
boolean onUnbind(Intent intent); // déconnexion d'un client <br />
void onRebind(Intent intent)</div></td></tr></tbody></table></div>
</blockquote>
<p>Google a publié un post sur la méthode <code class="codecolorer text default"><span class="text">onStartCommand()</span></code> apparue avec le SDK 2.0 :</p>
<p>http://android-developers.blogspot.com/2010/02/service-api-changes-starting-with.html</p>
<p>La méthode <code class="codecolorer text default"><span class="text">onStart()</span></code> est dépréciée mais doit être redéfinie pour une compatibilité avec les SDK antérieurs (si nécessaire).</p>
<p>Quant au cycle de vie d&rsquo;un service, Google l&rsquo;illustre de la manière suivante :<br />
<img src="http://developer.android.com/images/service_lifecycle.png"/></p>
<p>Pour interagir (demarrer/arrêter&#8230;) avec un service, deux possibilités s&rsquo;offrent à nous :</p>
<ul>
<li>Soit on appelle la méthode <code class="codecolorer text default"><span class="text">startService()</span></code> qui invoque la méthode <code class="codecolorer text default"><span class="text">onCreate()</span></code> puis <code class="codecolorer text default"><span class="text">onStart()</span></code><br />
<strong>service.startService() | -> onCreate() &#8211; > onStartCommand() [service running]</strong><br />
L&rsquo;appel de la méthode <code class="codecolorer text default"><span class="text">stopService()</span></code> invoque la méthode onDestroy()</p>
</li>
<li>Soit on appelle la méthode <code class="codecolorer text default"><span class="text">bindService()</span></code> qui appelle uniquement la méthode <code class="codecolorer text default"><span class="text">onCreate()</span></code><br />
<strong>activity.bindService() | ->onCreate() [service created]</strong></li>
</ul>
<p>
Il est possible de voir la liste des services exécutés en allant dans <em>Menu > Settings > Applications > Running Services ></em> du téléphone:</p>
<p><img src="http://blog.developpez.com/media/services-liste.png"/></p>
<p></p>
<h1 id="localservice"></h1>
<p><strong>> Services locaux (LocalService)</strong></p>
<p>    <strong>>> Déclaration du service</strong></p>
<p>Un service Local n&rsquo;est accessible que par les <code class="codecolorer text default"><span class="text">Activity</span></code> de l&rsquo;application.</p>
<p>Pour l&rsquo;exemple, notre service initialise un <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Timer.html">Timer</a> et une tâche qui sera exécutée toutes les minutes. Nous allons créer une classe héritant de <a href="http://developer.android.com/reference/android/app/Service.html">Service</a> et surcharger les méthodes <code class="codecolorer text default"><span class="text">onCreate()</span></code>, <code class="codecolorer text default"><span class="text">onStart()</span></code> et <code class="codecolorer text default"><span class="text">onDestroy()</span></code>.</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundService extends Service { <br />
&nbsp;<br />
private Timer timer ; <br />
&nbsp;<br />
@Override <br />
protected void onCreate() { <br />
&nbsp; &nbsp; super.onCreate(); <br />
&nbsp; &nbsp; timer = new Timer(); <br />
&nbsp; &nbsp; Log.d(this.getClass().getName(), &quot;onCreate&quot;); <br />
} <br />
&nbsp;<br />
@Override <br />
public int onStartCommand(Intent intent, int flags, int startId) { <br />
&nbsp; &nbsp; Log.d(this.getClass().getName(), &quot;onStart&quot;); <br />
&nbsp; &nbsp; timer.scheduleAtFixedRate(new TimerTask() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Executer de votre tâche <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; }, 0, 60000); <br />
&nbsp;<br />
&nbsp; &nbsp; return START_NOT_STICKY; <br />
} <br />
&nbsp;<br />
@Override <br />
public void onDestroy() { <br />
&nbsp; &nbsp; Log.d(this.getClass().getName(), &quot;onDestroy&quot;); <br />
&nbsp; &nbsp; this.timer.cancel(); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous déclarons le service dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> à l&rsquo;intérieur de la balise <code class="codecolorer text default"><span class="text">&lt;application&gt;</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;service android:name=&quot;.BackgroundService&quot; /&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Pour le démarrer, nous faisons appel à la méthode <code class="codecolorer text default"><span class="text">startService(Intent)</span></code> de l&rsquo;<code class="codecolorer text default"><span class="text">Activity</span></code> prenant en paramêtre un <a href="http://developer.android.com/reference/android/content/Intent.html">Intent</a>. Ce dernier peut être initialisé de deux manières :</p>
<ul>
<li>Soit en lui passant explicitement le context de l&rsquo;application et la class du service.</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(this,BackgroundService.class); <br />
startService(intent);</div></td></tr></tbody></table></div>
</blockquote>
</li>
<li>Soit en déclarant une action dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code>&#8230;<br />
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;service android:enabled=&quot;true&quot; android:name=&quot;.BackgroundService&quot;&gt; <br />
&nbsp; &nbsp; &lt;intent-filter&gt; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;action android:name=&quot;.BackgroundService.ACTION&quot; /&gt; <br />
&nbsp; &nbsp; &lt;/intent-filter&gt; <br />
&lt;/service&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>&#8230; que nous passons au constructeur de l&rsquo;intent</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(&quot;.BackgroundService.ACTION&quot;); &nbsp; &nbsp; <br />
startService(intent);</div></td></tr></tbody></table></div>
</blockquote>
</li>
</ul>
<p>Il est possible de passer des objets complets serialisables en paramètre avec les méthodes <a href="http://developer.android.com/reference/android/content/Intent.html#putExtra%28java.lang.String,%20android.os.Bundle%29">intent.putExtra(&#8230;)</a>. Les objets pourront être récupérer dans la méthode <code class="codecolorer text default"><span class="text">onStart(Intent intent, int startId)</span></code> du service avec <a href="http://developer.android.com/reference/android/os/Bundle.html#get%28java.lang.String%29">intent.getExtras().get(String key)</a>. Le fonctionnement est similaire à une table de Hash.</p>
<p>Pour arrêter notre service :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(this,BackgroundService.class); <br />
stopService(intent);</div></td></tr></tbody></table></div>
</blockquote>
<p></p>
<p>    <strong>>> Implémentation des listeners</strong></p>
<p>Dans un premier temps, nous allons implémenter un système de listeners très simple. L&rsquo;interface <code class="codecolorer text default"><span class="text">IBackgroundServiceListener</span></code> implémentée par notre Activity pour écouter les mises à jour du service est la suivante :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public interface IBackgroundServiceListener { <br />
&nbsp; &nbsp; public void dataChanged(Object o); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous créons l&rsquo;interface <code class="codecolorer text default"><span class="text">IBackgroundService</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public interface IBackgroundService { <br />
&nbsp; &nbsp; public void addListener(IBackgroundServiceListener listener); <br />
&nbsp; &nbsp; public void removeListener(IBackgroundServiceListener listener); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous implémentons l&rsquo;interface dans le service :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">private List&lt;IBackgroundServiceListener&gt; listeners = null; <br />
&nbsp;<br />
// Ajout d'un listener <br />
public void addListener(IBackgroundServiceListener listener) { <br />
&nbsp; &nbsp; if(listeners == null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; listeners = new ArrayList&lt;IGoogleWeatherListener&gt;(); <br />
&nbsp; &nbsp; } <br />
&nbsp; &nbsp; listeners.add(listener); <br />
} <br />
&nbsp;<br />
// Suppression d'un listener <br />
public void removeListener(IBackgroundServiceListener listener) { <br />
&nbsp; &nbsp; if(listeners != null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; listeners.remove(listener); <br />
&nbsp; &nbsp; } <br />
} <br />
&nbsp;<br />
// Notification des listeners <br />
private void fireDataChanged(Object data){ <br />
&nbsp; &nbsp; if(listeners != null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; for(IBackgroundServiceListener listener: listeners){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listener.dataChanged(data); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Pour accéder au service depuis l&rsquo;Activity nous rajoutons une variable static <code class="codecolorer text default"><span class="text">IBackgroundService service</span></code> que nous initialiserons dans la méthode <code class="codecolorer text default"><span class="text">onCreate()</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundService extends Service implements IBackgroundService { <br />
... <br />
&nbsp;<br />
&nbsp; &nbsp; private static IBackgroundService service; <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onCreate() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; ... <br />
&nbsp; &nbsp; &nbsp; &nbsp; service = this; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public static IBackgroundService getService() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return service; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
... <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>L&rsquo;emploi d&rsquo;une variable static n&rsquo;est pas la meilleure solution. Nous verrons plus tard comment nous en passer. Mais à ce stade, il n&rsquo;est pas possible d&rsquo;accéder aux méthodes du service et d&rsquo;appeler un setter.</p>
<p>Nous poursuivons en définissant notre listener dans notre Activity:</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new &nbsp;<br />
// Lancement du service <br />
Intent(this,BackgroundService.class); &nbsp;<br />
startService(intent); <br />
&nbsp;<br />
// Ajout de l'activity à la liste des listeners du service <br />
BackgroundService.getService().addListener(new IBackgroundServiceListener() { <br />
&nbsp; &nbsp; public void dataChanged(Object o) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; // mise à jour de l'interface graphique <br />
&nbsp; &nbsp; } <br />
});</div></td></tr></tbody></table></div>
</blockquote>
<p>Au moment voulu, dans la méthode <code class="codecolorer text default"><span class="text">run()</span></code> du Timer nous ferons appel à la méthode privée <code class="codecolorer text default"><span class="text">fireDataChanged(data)</span></code> pour notifier les listeners (dans notre cas, l&rsquo;Activity) de la mise à jour des données.</p>
<p><strong>Attention !</strong> Si nous nous contentons d&rsquo;implémenter la méthode <code class="codecolorer text default"><span class="text">dataChanged()</span></code>, l&rsquo;exception suivante sera levée au runtime :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">android.view.ViewRoot$CalledFromWrongThreadException: &nbsp;<br />
Only the original thread that created a view hierarchy can touch its views.</div></td></tr></tbody></table></div>
</blockquote>
<p>En effet, la méthode du listener <code class="codecolorer text default"><span class="text">dataChanged()</span></code> est appelée par le Timer, et donc exécutée dans le Thread de celui-ci. L&rsquo;exception nous indique que toute mise à jour de l&rsquo;interface utilisateur ne peut se faire que par le Thread responsable de la création des View, Button, TextView&#8230; Nous utilisons donc la méthode <a href="http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29">runOnUiThread</a> de la classe <a href="http://developer.android.com/reference/android/app/Activity.html">Activity</a> pour poster des instructions dans l&rsquo;UiThread : </p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">// Ajout de l'activity à la liste des listeners du service &nbsp;<br />
BackgroundService.getService().addListener(new IBackgroundServiceListener() { &nbsp;<br />
&nbsp; &nbsp; public void dataChanged(final Object o) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; MyActivity.this.runOnUiThread(new Runnable() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Mise à jour de l'UI <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; }); <br />
}});</div></td></tr></tbody></table></div>
</blockquote>
<p>Il nous reste à mettre à jour la méthode <code class="codecolorer text default"><span class="text">onDestroy()</span></code> du service pour vider la liste des listeners :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Override <br />
public void onDestroy() { <br />
&nbsp; &nbsp; this.listeners.clear(); <br />
&nbsp; &nbsp; this.timer.cancel(); <br />
&nbsp; &nbsp; Log.d(this.getClass().getName(), &quot;onDestroy&quot;); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>A ce stade, nous avons vu comment déclarer notre service et y accéder, définir une tâche de fond, et mettre à jour notre interface utilisateur. Le défaut de la solution proposée est l&rsquo;utilisation d&rsquo;une variable static pour accéder au service depuis l&rsquo;Activity. Malheureusement, la méthode <code class="codecolorer text default"><span class="text">startService(Intent)</span></code> n&rsquo;offre pas d&rsquo;accès direct aux méthodes du services. Pour se faire, nous allons avoir recours au binding.<br />
</p>
<p>    <strong>>> Implémentation d&rsquo;un binder</strong></p>
<p>Comme avec un RemoteService, nous allons nous connecter au service et récupérer un <a href="http://developer.android.com/reference/android/os/Binder.html">Binder</a>. A travers cet objet nous accéderons aux méthodes public du service via l&rsquo;interface <code class="codecolorer text default"><span class="text">IBackgroundService</span></code> que nous avons définie plus haut. L&rsquo;avantage de cette solution est d&rsquo;unifier l&rsquo;utilisation des LocalService et RemoteService mais surtout de récupérer l&rsquo;instance du service.</p>
<p>Nous redéfinissons la méthode <code class="codecolorer text default"><span class="text">onBind()</span></code>, modifions l&rsquo;implémentation du listener et déportons notre tâche de fond dans la méthode <code class="codecolorer text default"><span class="text">_onStart()</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundService extends Service implements IBackgroundService { <br />
&nbsp; <br />
&nbsp; &nbsp; private Timer timer ; <br />
&nbsp; <br />
&nbsp; &nbsp; private BackgroundServiceBinder binder ; &nbsp;<br />
&nbsp; &nbsp; private List&lt;IBackgroundServiceListener&gt; listeners = null; <br />
&nbsp; <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onCreate() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; timer = new Timer(); &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; binder = new BackgroundServiceBinder(this); &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; _onStart(); <br />
&nbsp; &nbsp; } <br />
&nbsp; <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public IBinder onBind(Intent intent) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return binder; <br />
&nbsp; &nbsp; } <br />
&nbsp; <br />
&nbsp; &nbsp; public void _onStart(){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; timer.scheduleAtFixedRate(new TimerTask() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(listeners != null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fireDataChanged(new Object()); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; }}, 0, 60000); <br />
&nbsp; &nbsp; } <br />
&nbsp; <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public int onStartCommand(Intent intent, int flags, int startId) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; _onStart(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; return START_NOT_STICKY; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; // Ajout d'un listener &nbsp;<br />
&nbsp; &nbsp; public void addListener(IBackgroundServiceListener listener) { &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; if(listeners == null){ &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listeners = new ArrayList&lt;IGoogleWeatherListener&gt;(); &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; listeners.add(listener); &nbsp;<br />
&nbsp; &nbsp; } &nbsp;<br />
&nbsp;<br />
&nbsp; &nbsp; // Suppression d'un listener &nbsp;<br />
&nbsp; &nbsp; public void removeListener(IBackgroundServiceListener listener) { &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; if(listeners != null){ &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listeners.remove(listener); &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;<br />
&nbsp; &nbsp; } &nbsp;<br />
&nbsp;<br />
&nbsp; &nbsp; // Notification des listeners &nbsp;<br />
&nbsp; &nbsp; private void fireDataChanged(Object data){ &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; if(listeners != null){ &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for(IBackgroundServiceListener listener: listeners){ &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listener.dataChanged(data); &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;<br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onDestroy() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.listeners.clear(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.timer.cancel(); <br />
&nbsp; &nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous définissons le binder qui nous permettra de récupérer l&rsquo;instance de notre service :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundServiceBinder extends Binder{ <br />
&nbsp; <br />
&nbsp; &nbsp; private IBackgroundService service = null; <br />
&nbsp; <br />
&nbsp; &nbsp; public BackgroundServiceBinder(IBackgroundService service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; super(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.service = service; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public IBackgroundService getService(){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; return service; <br />
&nbsp; &nbsp; } <br />
};</div></td></tr></tbody></table></div>
</blockquote>
<p>L&rsquo;avantage de l&rsquo;interface <code class="codecolorer text default"><span class="text">IBackgroundService</span></code> est de masquer les méthodes <code class="codecolorer text default"><span class="text">onCreate()</span></code>, <code class="codecolorer text default"><span class="text">onBind()...</span></code> et de ne proposer uniquement <code class="codecolorer text default"><span class="text">addListener()</span></code>, <code class="codecolorer text default"><span class="text">removeListener()</span></code> et pourquoi pas d&rsquo;autres méthodes métiers.</p>
<p>Il nous reste à  nous connecter au service,  accéder aux méthodes exposées par l&rsquo;interface et s&rsquo;ajouter comme listener pour mettre à jour l&rsquo;interface utilisateur : </p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(this,BackgroundService.class); <br />
&nbsp;<br />
final IBackgroundServiceListener listener = new IBackgroundServiceListener() { <br />
&nbsp; &nbsp; public void dataChanged(final Object data) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; MyActivity.this.runOnUiThread(new Runnable() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Mise à jour de l'UI &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; }); <br />
&nbsp; &nbsp; } <br />
}; <br />
&nbsp;<br />
ServiceConnection connection = new ServiceConnection() { <br />
&nbsp; &nbsp; public void onServiceConnected(ComponentName name, IBinder service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;BackgroundService&quot;, &quot;Connected!&quot;); <br />
&nbsp; &nbsp; &nbsp; &nbsp; IBackgroundService service = ((BackgroundServiceBinder)service).getService(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; service.addListener(listener); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public void onServiceDisconnected(ComponentName name) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;BackgroundService&quot;, &quot;Disconnected!&quot;); <br />
&nbsp; &nbsp; } &nbsp;<br />
}; <br />
&nbsp; &nbsp; <br />
bindService(intent,connection, Context.BIND_AUTO_CREATE);</div></td></tr></tbody></table></div>
</blockquote>
<p>La connexion au service se fait via la méthode <a href="http://developer.android.com/reference/android/content/Context.html#bindService%28android.content.Intent,%20android.content.ServiceConnection,%20int%29">bindService (Intent  service, ServiceConnection  conn, int flags)</a> de la classe <a href="http://developer.android.com/reference/android/content/Context.html">Context</a> dont hérite Activity. L&rsquo;argument flags peut prendre soit <code class="codecolorer text default"><span class="text">0</span></code> soit <a href="http://developer.android.com/reference/android/content/Context.html#BIND_AUTO_CREATE">Context.BIND_AUTO_CREATE</a> pour forcer le service à démarrer, s&rsquo;il ne l&rsquo;est pas, au moment de la connexion.</p>
<p>La partie sur les LocalService est terminée. Je ne suis pas personnellement convaincu de leur intéret. Les classes <a href="http://developer.android.com/reference/android/app/Service.html">Service</a>, <a href="http://developer.android.com/reference/android/content/ServiceConnection.html">ServiceConnection</a>, <a href="http://developer.android.com/reference/android/os/IBinder.html">IBinder</a>&#8230; sont relativement contraignantes, complexifient le code et n&rsquo;offrent pas vraiment d&rsquo;aide à la réalisation de services. Mais si un jour votre LocalService doit se transformer en RemoteService alors vous serez prets.</p>
<h1 id="remoteservice"></h1>
<p><strong>> Services distants (RemoteService)</strong></p>
<p>    <strong>>> Déclaration du service</strong></p>
<p>Contrairement aux LocalService qui s&rsquo;exécutent dans le processus de l&rsquo;application et plus particulièrement dans le thread principal, les RemoteService s&rsquo;exécutent dans un processus totalement différent de celui de votre application.</p>
<p>Afin que deux processus différents puissent communiquer et s&rsquo;échanger des données, Google a implémenté son <a href="http://fr.wikipedia.org/wiki/Interface_description_language">IDL</a> appelé AIDL (Android Interface Definition Language) pour décrire les méthodes publiques et les données échangées. Cette description se fait dans un fichier <strong>.aidl</strong> qui devra être connu des deux processus !</p>
<p>Pourront être échangées entre deux processus, seulement les types primitifs java (<code class="codecolorer text default"><span class="text">int</span></code>, <code class="codecolorer text default"><span class="text">boolean</span></code>, etc), les <code class="codecolorer text default"><span class="text">String</span></code>, <code class="codecolorer text default"><span class="text">List</span></code>, <code class="codecolorer text default"><span class="text">Map</span></code>et <code class="codecolorer text default"><span class="text">CharSequence</span></code>. Pour des objects personnalisés, vous devrez implémenter l&rsquo;interface <a href="http://developer.android.com/guide/developing/tools/aidl.html#parcelable">Parcelable</a>. </p>
<p>Un RemoteService peut être sollicité par plusieurs applications différentes. C&rsquo;est pourquoi je préfère le déporter dans un nouveau projet Android sous Eclipse. Je conseille également la réalisation d&rsquo;une petite interface graphique avec deux boutons pour le démarrer et l&rsquo;arrêter. Si vous ne souhaitez pas créer un projet particulier, il faudra ajouter <code class="codecolorer text default"><span class="text">android:process=&quot;:remote&quot;</span></code> à la déclaration de votre service (<code class="codecolorer text default"><span class="text">&lt;service&gt;</span></code>) dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code>.</p>
<p>Après avoir créer un projet BackgroundService (package de l&rsquo;application <code class="codecolorer text default"><span class="text">com.android23.backgroundservice</span></code>), nous créons notre service <code class="codecolorer text default"><span class="text">com.android23.backgroundservice.service.BackgroundService</span></code> le plus simple qui soit, avec un <code class="codecolorer text default"><span class="text">Timer</span></code> et un objet <code class="codecolorer text default"><span class="text">Data</span></code>:</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundService extends Service &nbsp;{ <br />
&nbsp;<br />
&nbsp; &nbsp; private Timer timer ; <br />
&nbsp; &nbsp; private BackgroundServiceBinder binder ; <br />
&nbsp; &nbsp; private Data data; <br />
&nbsp; <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onCreate() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; binder = new BackgroundServiceBinder(this); <br />
&nbsp; &nbsp; &nbsp; &nbsp; timer = new Timer(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; _onStart(); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public void _onStart(){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; timer.scheduleAtFixedRate(new TimerTask() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = new Data(18, Data.Weather.ENSOLEILLE); &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; }, 0, 60000); <br />
&nbsp; &nbsp; } <br />
&nbsp; <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onDestroy() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; super.onDestroy(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.binder = null; <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.timer.cancel(); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public IBinder onBind(Intent intent) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return binder; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public Data getData() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return data; <br />
&nbsp; &nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Pour chaque RemoteService il est nécessaire de déclarer un fichier .aidl que nous plaçons dans le même package (<code class="codecolorer text default"><span class="text">com.android23.backgroundservice.service</span></code>).</p>
<p>Voici le contenu de notre fichier <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.aidl</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">package com.android23.backgroundservice.service; <br />
&nbsp;<br />
import com.android23.backgroundservice.bo.Data; <br />
&nbsp;<br />
interface IRemoteBackgroundService { &nbsp;<br />
&nbsp; &nbsp; int getPid(); // Renvoie le PID du processus du service <br />
&nbsp; &nbsp; Data getData(); // Renvoie notre objet mis à jour <br />
&nbsp; &nbsp; //void updateData(in Data data); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Il n&rsquo;y pas de règle concernant le nom du fichier. Ce fichier sert d&rsquo;interface entre le client et le service. Nous y déclarons seulement les méthodes public. La <a href="http://developer.android.com/guide/developing/tools/aidl.html#aidlsyntax">syntaxe</a> ressemble fortement au Java bien que cela n&rsquo;en soit pas. Les mots clefs &lsquo;<code class="codecolorer text default"><span class="text">in</span></code>&lsquo;, &lsquo;<code class="codecolorer text default"><span class="text">out</span></code>&lsquo;, &lsquo;<code class="codecolorer text default"><span class="text">inout</span></code>&lsquo; sont ajoutés aux paramètres des méthodes. &lsquo;<code class="codecolorer text default"><span class="text">//</span></code>&lsquo; permet d&rsquo;ajouter des commentaires. Enfin on peut spécifier &lsquo;<code class="codecolorer text default"><span class="text">oneway</span></code>&lsquo; devant <code class="codecolorer text default"><span class="text">interface</span></code>.</p>
<p>Nous créons dans le package <code class="codecolorer text default"><span class="text">com.android23.backgroundservice.bo</span></code>, notre objet java Data implémentant l&rsquo;interface <a href="http://developer.android.com/reference/android/os/Parcelable.html">Parcelable</a> : </p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class Data implements Parcelable { <br />
&nbsp; &nbsp; public static enum Weather {NUAGEUX,PLUVIEUX,ENSOLEILLE,BROUILLARD} <br />
&nbsp; &nbsp; private int temperature; <br />
&nbsp; &nbsp; private Weather today; <br />
&nbsp; <br />
&nbsp; &nbsp; public static final Parcelable.Creator&lt;Data&gt; CREATOR = new Parcelable.Creator&lt;Data&gt;() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; public Data createFromParcel(Parcel in) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new Data(in); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; public Data[] newArray(int size) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new Data[size]; <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; }; <br />
&nbsp;<br />
&nbsp; &nbsp; public Data(int temperature, Weather today) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; super(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.temperature = temperature; <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.today = today; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public int getTemperature() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return temperature; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public Weather getToday() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return today; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; private Data(Parcel in) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; readFromParcel(in); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public void readFromParcel(Parcel in) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; temperature = in.readInt(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; today = (Weather)in.readSerializable(); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public int describeContents() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return 0; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public void writeToParcel(Parcel arg0, int arg1) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; arg0.writeInt(temperature); <br />
&nbsp; &nbsp; &nbsp; &nbsp; arg0.writeSerializable(today); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Notre objet contient le temps et la température, deux types java simples. Le code est calqué sur l&rsquo;exemple que vous trouverez sur <a href="http://developer.android.com/guide/developing/tools/aidl.html">http://developer.android.com/guide/developing/tools/aidl.html</a>.</p>
<p>L&rsquo;interface Parcelable nous oblige à définir la méthode <code class="codecolorer text default"><span class="text">writeToParcel()</span></code> dans laquelle nous ajoutons à l&rsquo;objet <a href="http://developer.android.com/reference/android/os/Parcel.html">Parcel</a> tous les attributs de notre objet <code class="codecolorer text default"><span class="text">Data</span></code>. Un <a href="http://developer.android.com/reference/android/os/Parcel.html">Parcel</a> peut être vue comme l&rsquo;unité de communication entre deux processus. Attention, l&rsquo;ordre dans lequel nous ajoutons les attributs à une importance. C&rsquo;est ordre doit être respecté dans la méthode <code class="codecolorer text default"><span class="text">readFromParcel()</span></code>.</p>
<p>L&rsquo;attribut static <code class="codecolorer text default"><span class="text">CREATOR</span></code> est obligatoire. Il implémente l&rsquo;interface <a href="http://developer.android.com/reference/android/os/Parcelable.Creator.html">Parcelable.Creator</a>. L&rsquo;implémentation de cette interface nous force a écrire la méthode <code class="codecolorer text default"><span class="text">readFromParcel()</span></code>.</p>
<p>Pour que notre objet <code class="codecolorer text default"><span class="text">Data</span></code> puisse voyager d&rsquo;un processus à l&rsquo;autre, il nous reste à déclarer son fichier <code class="codecolorer text default"><span class="text">Data.aidl</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">package com.android23.backgroundservice.bo; <br />
parcelable Data;</div></td></tr></tbody></table></div>
</blockquote>
<p>Le mot clef <code class="codecolorer text default"><span class="text">parcelable</span></code> spécifie que notre objet <code class="codecolorer text default"><span class="text">Data</span></code> implémente bien le protocole Google pour les communication IPC.</p>
<p>Les erreurs contenues dans les fichiers .aidl devraient disparaitre. Eclipse génère alors l&rsquo;interface Java <code class="codecolorer text default"><span class="text">IRemoteBackgroundService</span></code> dans le répertoire source &laquo;&nbsp;gen&nbsp;&raquo; du projet :<br />
<img src="http://blog.developpez.com/media/remote-service.png"/></p>
<p>Il nous reste ensuite à déclarer notre <code class="codecolorer text default"><span class="text">BackgroundServiceBinder</span></code> qui permettra au client d&rsquo;accéder aux méthodes déclarées dans le fichier aidl. Notre binder hérite de <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.Stub</span></code> généré par Eclipse :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundServiceBinder extends IRemoteBackgroundService.Stub{ <br />
&nbsp;<br />
&nbsp; &nbsp; private BackgroundService service = null; <br />
&nbsp; <br />
&nbsp; &nbsp; public BackgroundServiceBinder(BackgroundService service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; super(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.service = service; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public Data getData() throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return service.getData(); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; public int getPid() throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return Process.myPid(); <br />
&nbsp; &nbsp; } <br />
};</div></td></tr></tbody></table></div>
</blockquote>
<p>N&rsquo;oublions pas de déclarer notre service dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;@string/app_name&quot;&gt; <br />
&nbsp; &nbsp; &lt;service android:exported=&quot;true&quot; android:name=&quot;.service.BackgroundService&quot; /&gt; <br />
&lt;/application&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Il est possible d&rsquo;ajouter une permission :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;permission android:name=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot; /&gt; <br />
&lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;BackgroundService&quot;&gt; <br />
&nbsp; &nbsp; &lt;service android:exported=&quot;true&quot; android:enabled=&quot;true&quot; <br />
&nbsp; &nbsp; &nbsp; android:name=&quot;.service.BackgroundService&quot; android:permission=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot;/&gt; <br />
&lt;/application&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Les clients devront déclarer cette permission dans leur fichier manifest pour se connecter au service. Sans quoi une <a href="http://developer.android.com/reference/java/lang/SecurityException.html">SecurityException</a> sera levée à l&rsquo;appel des méthodes <code class="codecolorer text default"><span class="text">startService</span></code> ou <code class="codecolorer text default"><span class="text">bindService</span></code>. Pour plus d&rsquo;informations concernant les permissions, jetez un coup d&rsquo;oeil à la documentation Google: </p>
<ul>
<li><a href="http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms">http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms</a></li>
<li><a href="http://developer.android.com/guide/topics/security/security.html">http://developer.android.com/guide/topics/security/security.html</a></li>
</ul>
<p>Revenons à notre projet Eclipse principal. Pour réaliser notre appel distant au service, nous devons y copier/coller les fichier .aidl <code class="codecolorer text default"><span class="text">Data.aidl</span></code>, et <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.aidl</span></code> dans le même package que le projet <code class="codecolorer text default"><span class="text">BackgroundService</span></code>, ainsi que votre objet java <code class="codecolorer text default"><span class="text">Data</span></code>! Eclipse créera automatiquement les interfaces d&rsquo;accès.</p>
<p>Pour nous connecter au service, rien de bien compliqué :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(); <br />
intent.setClassName(&quot;com.android23.backgroundservice&quot;, &quot;com.android23.backgroundservice.service.BackgroundService&quot;); <br />
&nbsp;<br />
ServiceConnection remoteConnection &nbsp;= new ServiceConnection() { &nbsp;<br />
&nbsp; &nbsp; public void onServiceDisconnected(ComponentName name) { &nbsp;<br />
&nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; public void onServiceConnected(ComponentName name, IBinder service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; IRemoteBackgroundService service = IRemoteBackgroundService.Stub.asInterface(service); <br />
&nbsp; &nbsp; &nbsp; &nbsp; try { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service.getPid(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service.getData(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } catch (RemoteException e) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // TODO Auto-generated catch block <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; } <br />
}; <br />
&nbsp;<br />
bindService(intent, remoteConnection, Context.BIND_AUTO_CREATE);</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous passons au constructeur de l&rsquo;<a href="http://developer.android.com/reference/android/content/Intent.html">Intent</a> le package principal de notre projet <code class="codecolorer text default"><span class="text">BackgroundService</span></code> puis la classe du service.<br />
<code class="codecolorer text default"><span class="text">IRemoteBackgroundService.Stub.asInterface()</span></code> nous permet de récupérer notre service. A noter également que chaque appel de méthode peut lever une <code class="codecolorer text default"><span class="text">RemoteException</span></code>.</p>
<p>Pour nous déconnecter au service, nous faisons appel à la méthode <code class="codecolorer text default"><span class="text">unbindService(ServiceConnection)</span></code> qui prend en paramêtre l&rsquo;object <code class="codecolorer text default"><span class="text">ServiceConnection</span></code> que nous avons créé :</p>
<blockquote><p><code class="codecolorer text default"><span class="text">unbindService(remoteConnection);</span></code></p></blockquote>
<p>Il est indispensable d&rsquo;ajouter la permission dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code>, au même niveau que la balise <code class="codecolorer text default"><span class="text">&lt;application&gt;</span></code> sans quoi notre <code class="codecolorer text default"><span class="text">Activity</span></code> ne se connectera jamais :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;uses-permission android:name=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot;&gt;&lt;/uses-permission&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Il n&rsquo;est pas possible de débugger à la fois le service et notre application. Pour tester, notre service il sera nécessaire de développer dans notre projet <code class="codecolorer text default"><span class="text">BackgroundService</span></code> une Activity avec 2 boutons :  un pour démarrer le service, et l&rsquo;autre pour l&rsquo;arrêter.<br />
Pour tester notre application, nous déployons le service en premier puis nous lançons notre application en mode debug.<br />
</p>
<p>    <strong>>> Mise en place d&rsquo;un syteme de Callback/Listeners</strong></p>
<p>Comme pour les LocalService, les RemoteService peuvent également notifier leurs clients grâce une liste <a href="http://developer.android.com/reference/android/os/RemoteCallbackList.html">RemoteCallbackList</a>. Pour cela nous allons apporter quelques modifications.</p>
<p>Pour commencer, retournons dans le projet BackgroundService, et modifions le fichier <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.aidl</span></code> en supprimant la méthode <code class="codecolorer text default"><span class="text">getData()</span></code> et en rajoutant les méthodes <code class="codecolorer text default"><span class="text">registerCallback</span></code> et <code class="codecolorer text default"><span class="text">unregisterCallback</span></code> qui permettrons aux clients de s&rsquo;enregistrer auprès du service :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">package com.android23.backgroundservice.service; <br />
&nbsp;<br />
import com.android23.backgroundservice.service.IRemoteBackgroundServiceCallback; <br />
&nbsp;<br />
interface IRemoteBackgroundService { &nbsp;<br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; int getPid(); <br />
&nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; void registerCallback(IRemoteBackgroundServiceCallback cb); <br />
&nbsp; &nbsp; void unregisterCallback(IRemoteBackgroundServiceCallback cb); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>A travers cette interface, les clients pourront s&rsquo;enregistrer. Une fois enregistrés il leur faudra implémenter l&rsquo;interface <code class="codecolorer text default"><span class="text">IRemoteBackgroundServiceCallback</span></code> que nous déclarons dans le fichier <code class="codecolorer text default"><span class="text">IRemoteBackgroundServiceCallback.aidl</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">package com.android23.backgroundservice.service; <br />
&nbsp;<br />
import com.android23.backgroundservice.bo.Data; <br />
&nbsp;<br />
interface IRemoteBackgroundServiceCallback { &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; void dataChanged(in Data data); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous déclarons dans notre service, la liste des clients à l&rsquo;écoute :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">final RemoteCallbackList&lt;IRemoteBackgroundServiceCallback&gt; callbacks = new RemoteCallbackList&lt;IRemoteBackgroundServiceCallback&gt;(); <br />
&nbsp;<br />
&nbsp; &nbsp; public RemoteCallbackList&lt;IRemoteBackgroundServiceCallback&gt; getCallbacks() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return callbacks; <br />
&nbsp; &nbsp; }</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous mettons à jour la méthode <code class="codecolorer text default"><span class="text">_onStart()</span></code> afin de créer un nouvel objet Data toutes les minutes et notifier les clients :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public void _onStart() { <br />
&nbsp; &nbsp; Log.i(getClass().getSimpleName(), &quot;_onStart()&quot;); <br />
&nbsp; &nbsp; timer.scheduleAtFixedRate(new TimerTask() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; public void run() { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = new Data(++counter, Data.Weather.BROUILLARD); <br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Broadcast to all clients the new value. <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final int N = callbacks.beginBroadcast(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &lt; N; i++) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callbacks.getBroadcastItem(i).dataChanged(data); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; catch (RemoteException e) {} <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; callbacks.finishBroadcast(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; }, 0,60000); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p><a href="http://developer.android.com/reference/android/os/RemoteCallbackList.html#beginBroadcast%28%29">callbacks.beginBroadcast()</a> et <code class="codecolorer text default"><span class="text">callbacks.finishBroadcast();</span></code> encadre le début et la fin de la notification des clients. <a href="http://developer.android.com/reference/android/os/RemoteCallbackList.html#getBroadcastItem%28int%29">callbacks.getBroadcastItem(i)</a> vous permet d&rsquo;appeler les méthodes de l&rsquo;intarface <code class="codecolorer text default"><span class="text">IRemoteBackgroundServiceCallback</span></code> et prévenir les clients. Elle ne peut être appelée qu&rsquo;après <code class="codecolorer text default"><span class="text">beginBroadcast()</span></code></p>
<p>Nous désactivons la liste des callbacks dans la méthode <code class="codecolorer text default"><span class="text">onDestroy()</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@Override <br />
public void onDestroy() { <br />
&nbsp; &nbsp; super.onDestroy(); <br />
&nbsp; &nbsp; this.binder = null; <br />
&nbsp; &nbsp; this.callbacks.kill(); // Désactive tous les éléments de la liste <br />
&nbsp; &nbsp; this.timer.cancel(); <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>N&rsquo;oublions pas de mettre à jour notre Binder implémentant l&rsquo;interface <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.Stub</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundServiceBinder extends IRemoteBackgroundService.Stub{ <br />
&nbsp;<br />
&nbsp; &nbsp; private BackgroundService service = null; <br />
&nbsp; <br />
&nbsp; &nbsp; public BackgroundServiceBinder(BackgroundService service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; super(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; this.service = service; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public int getPid() throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; return Process.myPid(); <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void registerCallback(IRemoteBackgroundServiceCallback cb) throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; if(cb != null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service.getCallbacks().register(cb); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void unregisterCallback(IRemoteBackgroundServiceCallback cb) throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; if(cb != null){ <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; service.getCallbacks().unregister(cb); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; } <br />
};</div></td></tr></tbody></table></div>
</blockquote>
<p>Retournons maintenant du côté de notre application principale pour y copier les fichiers <code class="codecolorer text default"><span class="text">IRemoteBackgroundService.aidl</span></code> et <code class="codecolorer text default"><span class="text">IRemoteBackgroundServiceCallback.aidl</span></code> dans le package <code class="codecolorer text default"><span class="text">com.android23.backgroundservice.service</span></code>.</p>
<p>Puis mettons à jour la manière dont nous nous connectons au service en déclarant un <code class="codecolorer text default"><span class="text">IRemoteBackgroundServiceCallback</span></code> et en l&rsquo;inscrivant auprès du service dans la méthode <code class="codecolorer text default"><span class="text">onServiceConnected()</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(); <br />
intent.setClassName(&quot;com.android23.backgroundservice&quot;, &quot;com.android23.backgroundservice.service.BackgroundService&quot;); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
final IRemoteBackgroundServiceCallback callback = new IRemoteBackgroundServiceCallback.Stub() { <br />
&nbsp; &nbsp; public void dataChanged(Data data) throws RemoteException { <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.d( getClass().getSimpleName(), &quot;Temperature : &quot; + data.getTemperature()); <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.d( getClass().getSimpleName(), &quot;Temps : &quot; + data.getToday().toString()); <br />
&nbsp; &nbsp; } <br />
}; <br />
&nbsp;<br />
ServiceConnection remoteConnection &nbsp;= new ServiceConnection() { <br />
&nbsp; &nbsp; public void onServiceDisconnected(ComponentName name) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; Log.d( getClass().getSimpleName(), &quot;onServiceConnected()&quot; ); <br />
&nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; public void onServiceConnected(ComponentName name, IBinder service) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; remoteBackgroundService = IRemoteBackgroundService.Stub.asInterface(service); <br />
&nbsp; &nbsp; &nbsp; &nbsp; try { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; remoteBackgroundService.getPid(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; remoteBackgroundService.registerCallback(callback); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp; &nbsp; &nbsp; &nbsp; catch (RemoteException e) {} <br />
&nbsp; &nbsp; } <br />
}; <br />
&nbsp;<br />
bindService(intent, remoteConnection, Context.BIND_AUTO_CREATE);</div></td></tr></tbody></table></div>
</blockquote>
<p>Pour tester, nous déployons le service et puis nous lançons en mode debug notre application.<br />
</p>
<p>    <strong>>> Démarrer le service au boot du téléphone</strong></p>
<p>Dans le projet BackgroundService, nous rajoutons un <a href="http://developer.android.com/reference/android/content/BroadcastReceiver.html">BroadcastReceiver</a> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public class BackgroundServiceReceiver extends BroadcastReceiver{ <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onReceive(Context context, Intent intent) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; Intent serviceIntent = new Intent(); <br />
&nbsp; &nbsp; &nbsp; &nbsp; serviceIntent.setClassName(&quot;com.android23.backgroundservice&quot;, &quot;com.android23.backgroundservice.service.BackgroundService&quot;); <br />
&nbsp; &nbsp; &nbsp; &nbsp; context.startService(serviceIntent); <br />
&nbsp; &nbsp; } <br />
}</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous le rajoutons dans le fichier <code class="codecolorer text default"><span class="text">AndroidManifest.xml</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;permission android:name=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot; /&gt; <br />
&nbsp;<br />
&lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;@string/app_name&quot;&gt; &nbsp;<br />
&nbsp;<br />
&nbsp; &nbsp; &lt;service android:exported=&quot;true&quot; android:name=&quot;.service.BackgroundService&quot; android:permission=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot; /&gt; &nbsp;<br />
&nbsp;<br />
&nbsp; &nbsp; &lt;receiver android:name=&quot;.service.BackgroundServiceReceiver&quot;&gt; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;intent-filter&gt; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot; /&gt; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;category android:name=&quot;android.intent.category.HOME&quot; /&gt; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/intent-filter&gt; <br />
&nbsp; &nbsp; &lt;/receiver&gt; <br />
&lt;/application&gt; <br />
&nbsp;<br />
&lt;uses-permission android:name=&quot;com.android23.backgroundservice.BACKGROUNDSERVICE_PERMISSION&quot;/&gt;</div></td></tr></tbody></table></div>
</blockquote>
<p>Nous avons également précisé que notre service demande la permission <code class="codecolorer text default"><span class="text">BACKGROUNDSERVICE_PERMISSION</span></code> pour pouvoir démarrer tout seul.</p>
<p>Déployons le service sur l&rsquo;émulateur en faisant un clic droit sur le projet : <em>Run As > Android Application</em>. Une fois déployé, re-démarrons l&rsquo;émulateur en cliquant sur le bouton <img src="http://blog.developpez.com/media/avd-manager-bouton.PNG" align="top" /> dans la barre d&rsquo;outil d&rsquo;Eclipse puis sur le bouton <strong>start</strong> dans la fenêtre qui apparait. Une fois l&rsquo;émulateur lancé, il suffit d&rsquo;aller dans <em>Menu > Settings > Applications > Running Services ></em> pour constater que notre <code class="codecolorer text default"><span class="text">BackgroundService</span></code> est bien en route.<br />
</p>
<p>    <strong>>> Exemple : Connexion au service MP3</strong></p>
<p>Le lecteur MP3 d&rsquo;Android est lui aussi un RemoteService. Il est donc très simple de s&rsquo;y connecter pour récupérer la chanson en cours, l&rsquo;artiste&#8230; Pour l&rsquo;exemple n&rsquo;oubliez pas de lancer la lecture d&rsquo;un fichier mp3.</p>
<p>Profitons du fait qu&rsquo;Android soit open-source pour nous rendre sur <a href="http://www.google.fr/codesearch">Google Code</a> et récupérer le fichier <a href="http://www.google.fr/codesearch/p?hl=fr#kYM2IxnT114/src/com/android/music/IMediaPlaybackService.aidl&#038;q=IMediaPlaybackService.aidl&#038;sa=N&#038;cd=1&#038;ct=rc">IMediaPlaybackService.aidl</a>.</p>
<p>Une fois le fichier récupéré, nous créons le package <code class="codecolorer text default"><span class="text">&quot;com.android.music&quot;</span></code> dans lequel nous insérons le fichier .aidl. Eclipse génère alors automatiquement l&rsquo;interface <code class="codecolorer text default"><span class="text">IMediaPlaybackService</span></code>. Le service ne renvoyant que des types primitifs, seul ce fichier suffit au client. </p>
<p>Nous nous connectons au lecteur mp3 comme nous nous connections à notre <code class="codecolorer text default"><span class="text">BackgroundService</span></code> :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Intent intent = new Intent(); <br />
intent.setClassName(&quot;com.android.music&quot;, &quot;com.android.music.MediaPlaybackService&quot;); <br />
&nbsp;<br />
ServiceConnection conn = new ServiceConnection() { <br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onServiceDisconnected(ComponentName componentname) { <br />
&nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; @Override <br />
&nbsp; &nbsp; public void onServiceConnected(ComponentName componentname, IBinder ibinder) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; IMediaPlaybackService service = IMediaPlaybackService.Stub.asInterface(ibinder); <br />
&nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; try { <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;ServiceConnection&quot;, &quot;Playing track: &quot; + service.getTrackName()); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Log.i(&quot;ServiceConnection&quot;, &quot;By artist: &quot; + service.getArtistName()); <br />
&nbsp; &nbsp; &nbsp; &nbsp; } catch (Exception e) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; } <br />
&nbsp;<br />
&nbsp; &nbsp; } <br />
}; <br />
&nbsp;<br />
this.bindService(intent, conn, 0);</div></td></tr></tbody></table></div>
</blockquote>
<p>Malheureusement, si Google modifie l&rsquo;interface .aidl de son lecteur MP3 il faudra mettre à jour le fichier <code class="codecolorer text default"><span class="text">IMediaPlaybackService.aidl</span></code> dans notre projet.<br />
</p>
<p><strong>> Conclusion</strong></p>
<p>Le post est relativement long mais regroupe tout ce que j&rsquo;ai pu apprendre en parcourant le net. Je vais réfléchir à le publier au format PDF dans la rubrique tutorial.</p>
<p>L&rsquo;intérêt des LocalService reste flou à mes yeux. J&rsquo;ai la fâcheuse impression de gagner plus en complexité qu&rsquo;en productivité. A contrario, les RemoteService ont une véritable valeur ajoutée. Ainsi on peut imaginer un service météo commun à toutes nos applications et nos widgets. Les possibilités sont nombreuses dès lors que l&rsquo;OS gère le multi-taches <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /> <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /> <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /></p>
<p>Si vous souhaitez que certains points soient approfondis, ou corrigés, n&rsquo;hésitez pas à m&rsquo;en faire part.<br />
</p>
<p><strong>> Ressources</strong></p>
<ul>
<li>
<p>http://developer.android.com/reference/android/app/Service.html</li>
<li>
<p>http://blog.7touchgroup.com/tag/android-services-versus-broadcast-receivers/</li>
<li>
<p>http://www.brighthub.com/mobile/google-android/articles/34861.aspx</p>
</li>
<li>
<p>http://saigeethamn.blogspot.com/2009/09/android-developer-tutorial-part-9.html</p>
</li>
<li>
<p>http://www.androidcompetencycenter.com/2009/06/start-service-at-boot/</p>
</li>
<li>
<p>http://www.alexc.me/android-music-app-service-currently-playing-song/231/</p>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Simuler le GPS avec le SDK d&#8217;Android</title>
		<link>https://blog.developpez.com/ndruet/p8494/android/simuler_le_gps_avec_le_sdk</link>
		<comments>https://blog.developpez.com/ndruet/p8494/android/simuler_le_gps_avec_le_sdk#comments</comments>
		<pubDate>Fri, 08 Jan 2010 10:42:15 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Dans le cadre du développement de ma première application sous Android 1.6 puis sous 2.01, j’ai voulu simuler le changement de coordonnées GPS. Je ne pensais pas que ce serait si problématique et qu’il y avait si peu de ressources sur le sujet. Quelques posts sur le Google Code m’ont tout de même aidé. > Environnement : Windows XP, Eclipse 3.5, ADT 0.9.5 et le SDK Android 1.6/2.0.1 En théorie, sous Eclipse, il suffit d’ouvrir [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Dans le cadre du développement de ma première application sous Android 1.6 puis sous 2.01, j’ai voulu simuler le changement de coordonnées GPS. Je ne pensais pas que ce serait si problématique et qu’il y avait si peu de ressources sur le sujet. Quelques posts sur le <a href="http://code.google.com/p/android/">Google Code</a> m’ont tout de même aidé.</p>
<p><span id="more-2"></span><br />
<br />
<strong>> Environnement : Windows XP, Eclipse 3.5, ADT 0.9.5 et le SDK Android 1.6/2.0.1</strong></p>
<p>En théorie, sous Eclipse, il suffit d’ouvrir la perspective DDMS (Dalvik Debug Monitor Server) et de saisir les coordonnées dans la vue « Emulator Control »<br />
<img src="http://blog.developpez.com/media/ddms-gps.png" alt="ddms-gps" /></p>
<p>En mettant un point d’arrêt dans la méthode :</p>
<blockquote>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">public void onLocationChanged(Location location) { <br />
&nbsp; &nbsp; … <br />
&nbsp; }</div></td></tr></tbody></table></div>
</blockquote>
<p>On s’aperçoit que l’application réagit au premier essai, mais pas aux tentatives suivantes…</p>
<p>J’ouvre une invite de commande sous Windows et me connecte à l’émulateur  par Telnet pour saisir manuellement les coordonnées :<br />
<img src="http://blog.developpez.com/media/ddms-telnet.png" alt="ddms-telnet" /><br />
<img src="http://blog.developpez.com/media/ddms-coordonnees.png" alt="ddms-gps" /></p>
<p><code class="codecolorer text default"><span class="text">geo fix [longitude] [latitude] [altitude (optionnelle)]</span></code></p>
<p>Cette fois ci, l’application réagit à chaque changement !</p>
<p>Mais je constate une différence entre les coordonnées saisies précédemment et celles notées sous Eclipse :</p>
<blockquote><p>Location[mProvider=gps,mTime=1261440000000,<em>mLatitude=47.207835833333334</em>,<em>mLongitude=-1.5516668333333334</em>,<br />
mHasAltitude=false,mAltitude=0.0,mHasSpeed=false,mSpeed=0.0,mHasBearing=false,<br />
mBearing=0.0,mHasAccuracy=false,mAccuracy=0.0,mExtras=null]</p></blockquote>
<p>
<ins>[ Mise à jour : 25/01/2010 ]</ins><br />
Le problème existe depuis la version 0.9 du SDK et ne semble pas avoir été corrigé depuis.<br />
Le blog <a href="http://www.insideandroid.fr/post/2009/03/28/Simulation-du-GPS-avec-l-emulateur-android">http://www.insideandroid.fr</a> nous donne une explication :<br />
<blockquote>&laquo;&nbsp;&#8230;en France la longitude et la latitude sont envoyées sous le format &laquo;&nbsp;120,0832&nbsp;&raquo; au lieu de &laquo;&nbsp;120.0832&nbsp;&raquo;. Cela engendre un bug critique qui empêche la mise à jour des coordonnées dans votre émulateur.&nbsp;&raquo;</p></blockquote>
<p><strong>Deux solutions existent :</strong></p>
<ul>
<li>Sous XP, allez dans <em>Panneau de Configuration > Options régionales et linguistiques.</em> Dans <em>Standarts et Formats</em> sélectionnez &laquo;&nbsp;Anglais (Etats Unis)&nbsp;&raquo;.
</li>
<li>Ajouter la ligne <code class="codecolorer text default"><span class="text">-Duser.language=en</span></code> dans le fichier <code class="codecolorer text default"><span class="text">eclipse.ini</span></code>.</li>
</ul>
<p>Redémarrez Eclipse dans les deux cas, pour que les modifications soient prises en compte, et tout fonctionnera sans problème !</p>
<p>Merci à <code class="codecolorer text default"><span class="text">@lesgrumels</span></code> pour le commentaire <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_wink.gif" alt=";)" class="wp-smiley" /></p>
<p>
<strong>> Ressources </strong></p>
<ul>
<li><a href="http://code.google.com/p/android/issues/detail?id=2545">http://code.google.com/p/android/issues/detail?id=2545</a></li>
<li><a href="http://groups.google.co.jp/group/android-developers/browse_thread/thread/b460684fed89a516">http://groups.google.co.jp/group/android-developers/browse_thread/thread/b460684fed89a516</a></li>
<li><a href="http://groups.google.com/group/android-developers/browse_thread/thread/5360c665c92af02e/d1ee24b3377a47da#d1ee24b3377a47da">http://groups.google.com/group/android-developers/browse_thread/thread/5360c665c92af02e/d1ee24b3377a47da#d1ee24b3377a47da</a></li>
<li><a href="http://www.insideandroid.fr/post/2009/03/28/Simulation-du-GPS-avec-l-emulateur-android">http://www.insideandroid.fr/post/2009/03/28/Simulation-du-GPS-avec-l-emulateur-android</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Simuler une sd-card, ajouter des fichiers et y accéder dans une application Android</title>
		<link>https://blog.developpez.com/ndruet/p8541/android/creer_un_sd_card_ajouter_des_fichiers_et</link>
		<comments>https://blog.developpez.com/ndruet/p8541/android/creer_un_sd_card_ajouter_des_fichiers_et#comments</comments>
		<pubDate>Mon, 25 Jan 2010 10:29:06 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Au démarrage d&#8217;un projet, j&#8217;ai voulu utiliser/simuler une carte SD avec l&#8217;émulateur. Je l&#8217;aurai parié ! Tout n&#8217;a pas fonctionné comme prévu avec le SDK d&#8217;Android Heureusement pour nous, la ligne de commande existe. Dans ce billet je vais donc expliquer comment : Créer une carte SD en ligne de commande Créer une carte SD sous Eclipse Configurer Eclipse Ajouter des fichiers Lister les fichiers dans une application > Créer une carte SD en ligne [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Au démarrage d&rsquo;un projet, j&rsquo;ai voulu utiliser/simuler une carte SD avec l&rsquo;émulateur. Je l&rsquo;aurai parié ! Tout n&rsquo;a pas fonctionné comme prévu avec le SDK d&rsquo;Android <img src="https://blog.developpez.com/ndruet/wp-includes/images/smilies/icon_biggrin.gif" alt=":-D" class="wp-smiley" /> Heureusement pour nous, la ligne de commande existe.</p>
<p>Dans ce billet je vais donc expliquer comment :</p>
<ul>
<li><a href="#cmd">Créer une carte SD en ligne de commande</a></li>
<li><a href="#eclipse">Créer une carte SD sous Eclipse</a></li>
<li><a href="#conf">Configurer Eclipse</a></li>
<li><a href="#add">Ajouter des fichiers</a></li>
<li><a href="#liste">Lister les fichiers dans une application</a></li>
</ul>
<p><span id="more-4"></span><br />
</p>
<h1 id="cmd"></h1>
<p><strong>> Créer une carte SD en ligne de commande</strong></p>
<p>Ouvrez une ligne de commande Windows. Allez dans le répertoire où vous souhaitez la créer puis utilisez l&rsquo;outil du SDK <em>mksdcard</em>:<br />
<img src="http://blog.developpez.com/media/mksdcard-help.png" width="646" height="159" alt="mksdcard-help" /></p>
<p>Pour créer un carte <em>sd_card </em>d&rsquo;1 Go :<br />
<code class="codecolorer text default"><span class="text">&gt; mksdcard 1024M sd_card</span></code></p>
<p>Il est inutile de spécifier une extension au nom de la carte.</p>
<p>Puis sous Eclipse: <em>Run > Run configurations > Target</em><br />
Dans le champ &laquo;&nbsp;Additional Emulator Command Line Options&nbsp;&raquo;,ajoutez le paramètre :<br />
<code class="codecolorer text default"><span class="text">-sdcard &lt;filepath&gt;/&lt;filename&gt;</span></code></p>
<p><img src="http://blog.developpez.com/media/emulator-param-sdcard.png" width="569" height="162" alt="" /></p>
<p>On indique ainsi  pour chaque projet, s&rsquo;il nécessite une carte et laquelle.<br />
</p>
<h1 id="eclipse"></h1>
<p><strong>> Créer une carte SD sous Eclipse</strong></p>
<p>Il est possible de créer une carte SD lors de la création du <em>Android Virtual Device</em> (AVD).<br />
<img src="http://blog.developpez.com/media/eclipse-sdcard-avd.png" width="400" height="521" alt="" /></p>
<p>Les données restent persistantes. Si vous relancer l&rsquo;émulateur, les fichiers seront toujours présents.</p>
<p>Vous pouvez également  spécifier un fichier créé avec la commande <code class="codecolorer text default"><span class="text">mksdcard</span></code></p>
<p>De cette manière, tous vos projets déployés sur ce même AVD utiliseront cette carte SD. Sinon vous devez créer un AVD par projet et spécifier sur quel AVD vous voulez déployer chacun de vos projets.</p>
<p>Pour cela dans Eclipse: <em>Run > Run configurations > Target</em> et sélectionnez l&rsquo;AVD<br />
<img src="http://blog.developpez.com/media/avd-deploiement.png" width="567" height="147" alt="" /></p>
<p></p>
<h1 id="add"></h1>
<p><strong>> Ajouter des fichiers</strong></p>
<p>Vous pouvez voir le contenu de la carte SD dans la vue DDMS sous l&rsquo;onglet <em>File Explorer</em> :<br />
<img src="http://blog.developpez.com/media/file-explorer.png" width="450" height="103" alt="" /></p>
<p>Il est nécessaire que l&rsquo;émulateur soit lancé.</p>
<p>Pour ajouter des fichiers deux solutions existent mais qu&rsquo;une fonctionne (c&rsquo;est la magie de google):</p>
<ul>
<li>En ligne de commande, utiliser la commande <code class="codecolorer text default"><span class="text">adb push &lt;source path&gt; /sdcard/</span></code>:<br />
<img src="http://blog.developpez.com/media/adb-push-sdcard.png" width="578" height="86" alt="" /></p>
</li>
<li>Sous Eclipse, sélectionnez le répertoire <code class="codecolorer text default"><span class="text">sdcard</span></code> et cliquez sur le bouton de droite à coté des onglets :<br />
<img src="http://blog.developpez.com/media/pull-push-sdcard.png" width="446" height="25" alt="" /><br />
Malheureusement, j&rsquo;obtiens systématiquement le message <ins>Failed to push the item(s)</ins> dans la console
</li>
</ul>
<p>
J&rsquo;ai quand même un doute sur la prise en compte à chaud de nouveaux fichiers, du moins en ce qui concerne le lecteur mp3&#8230;</p>
<p></p>
<h1 id="liste"></h1>
<p><strong>> Lister les fichiers dans une application</strong></p>
<p>Je vais faire court puisque qu&rsquo;un très bon tutorial existe http://www.anddev.org/building_an_android_filebrowser_list-based_-t67.html et montre comment lister les fichiers du téléphone dans une <code class="codecolorer text default"><span class="text">ListActivity</span></code></p>
<p>Rien de bien sorcier, puisqu&rsquo;on part d&rsquo;un objet <code class="codecolorer text default"><span class="text">File</span></code> en indiquant la racine du téléphone :</p>
<blockquote><p><code class="codecolorer text default"><span class="text">File file = new File(&quot;/&quot;);</span></code></p></blockquote>
<p>Pour lister les fichiers de la carte SD, on partira de :</p>
<blockquote><p><code class="codecolorer text default"><span class="text">File file = new File(&quot;/sdcard/&quot;);</span></code></p></blockquote>
<p>On prendra soin de vérifier la présence de la carte SD avec <code class="codecolorer text default"><span class="text">file.exist();</span></code> qui renvoie vrai si le répertoire ou le fichier existe. On peut prendre également la précaution de s&rsquo;assurer que notre <code class="codecolorer text default"><span class="text">file</span></code>est un bien un répertoire en utilisant <code class="codecolorer text default"><span class="text">file.isDirectory();</span></code>.</p>
<p>Enfin n&rsquo;oublions pas :</p>
<blockquote><p><code class="codecolorer text default"><span class="text">file.listFiles();</span></code></p></blockquote>
<p>qui renvoie un tableau de <code class="codecolorer text default"><span class="text">File</span></code>présents dans le répertoire.</p>
<p>Pour compiler l&rsquo;exemple, effectuez les quelques modifications indiquées sur ce post : http://www.anddev.org/viewtopic.php?p=12328#12328</p>
<p>
<strong>> Ressources</strong></p>
<ul>
<li>
<p>http://www.anddev.org/tinytut_-_pull-push_files_from-to_the_emulator_eclipse-t113.html</li>
<li><a href="http://modmygphone.com/wiki/index.php/Emulating_SD_card">http://modmygphone.com/wiki/index.php/Emulating_SD_card</a></li>
<li><a href="http://www.anddev.org/building_an_android_filebrowser_list-based_-t67.html">http://www.anddev.org/building_an_android_filebrowser_list-based_-t67.html</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bienvenue</title>
		<link>https://blog.developpez.com/ndruet/p8497/android/bienvenue_sur_android23</link>
		<comments>https://blog.developpez.com/ndruet/p8497/android/bienvenue_sur_android23#comments</comments>
		<pubDate>Thu, 07 Jan 2010 16:02:23 +0000</pubDate>
		<dc:creator><![CDATA[ndruet]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Android23 est un blog en français entièrement consacré au développement sur l&#8217;OS pour Mobile de Google. J&#8217;y publierai essentiellement des bouts de code, mes déboires avec le SKD et un peu d&#8217;actualité autour du système. Je me présente : Nicolas, Développeur Java/J2EE depuis 3 en SSII à Nantes. Le choix de la plateforme s&#8217;est fait tout naturellement. J&#8217;ai pu rapidement trouver mes marques et réaliser quelques idées en Java sous Eclipse. L&#8217;accessibilité de l&#8217;environnement de [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Android23 est un blog en français entièrement consacré au développement sur l&rsquo;OS pour Mobile de Google. J&rsquo;y publierai essentiellement des bouts de code, mes déboires avec le SKD et un peu d&rsquo;actualité autour du système.</p>
<p><img src="http://blog.developpez.com/media/bg_android_01.png" /> </p>
<p>Je me présente : Nicolas, Développeur Java/J2EE depuis 3 en SSII à Nantes. Le choix de la plateforme s&rsquo;est fait tout naturellement. J&rsquo;ai pu rapidement trouver mes marques et réaliser quelques idées en Java sous Eclipse. L&rsquo;accessibilité de l&rsquo;environnement de développement et l&rsquo;arrivée du Google Phone NexusOne, je l&rsquo;espère, en motiveront plus d&rsquo;un à se lancer dans l&rsquo;aventure.</p>
<p>Après quelques applications j&rsquo;ai été confronté à un manque de ressources en français. C&rsquo;est la principale raison de ce blog. J&rsquo;ai donc du aller voir ce que proposait nos amis d&rsquo;outre-Atlantique. Si la documentation Google http://developer.android.com/guide/index.html est une véritable mine d&rsquo;or, je suis tombé sur http://www.anddev.org/index.php regorgeant d&rsquo;astuces, d&rsquo;exemples&#8230; et plein d&rsquo;autres que je citerai dans mes articles.</p>
<p>En dehors du forum http://www.developpez.net/forums/f1238/java/general-java/java-mobiles/android/ et quelques blogs http://jahbromo.blogspot.com/, http://android.cyrilmottier.com/ et http://android-france.fr/category/developpement/ je n&rsquo;ai pas trouvé grand chose.</p>
<p>J’espère donc que ce blog apportera quelques solutions à vos développements actuels et futurs.  N’hésitez surtout pas à laisser des commentaires. Cela me permettra d&rsquo;avoir un regard critique sur ce qui a été écrit et de corriger les erreurs qui se sont glissées lors de la rédaction des billets.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
