I. Installation d'Android▲
La première étape est bien sûr d'installer notre environnement de développement, un JDK en version 1.5 ou 1.6 est un prérequis de base. Le SDK du système d'exploitation pour mobile Android est disponible à l'adresse suivante : http://developer.android.com/index.html
En novembre 2009, la dernière version était la 2.0 ; les versions se succédant à une vitesse soutenue. Le SDK se présente sous forme d'un simple fichier zip, ou tgz pour les Linuxiens, qu'il suffit de décompresser. Cependant, depuis la version 2.0, des changements sont intervenus : désormais le SDK a été modularisé et ses composants sont téléchargeables individuellement grâce à un gestionnaire de packages qui se lance en exécutant « SDK Setup.exe » (présent à la racine du SDK Android).
Attention cependant, il se peut qu'une erreur se produise lors de la récupération du fichier repository.xml qui référence les composants de la plateforme. Si tel est le cas il faudra modifier son URL pour basculer de l'https à l'http :
Une fois ce problème potentiel réglé, il ne reste plus qu'à sélectionner les éléments à installer :
Cet outil de gestion des composants est en outre accessible directement depuis le plugin Eclipse Android (Android Development Tools). Pour les adeptes d'Eclipse, il ne sera donc pas nécessaire d'exécuter « SDK Setup.exe » pour gérer les éléments du SDK.
II. Installation du plugin Eclipse▲
Même s'il n'est pas indispensable et que n'importe quel autre éditeur ferait l'affaire, il peut être plus simple dans un premier temps tout au moins de développer avec l'aide du plugin Eclipse.
Pour l'installer, il suffit d'ajouter le site https://dl-ssl.google.com/android/eclipse/ dans la liste des dépôts de l'IDE.
III. Création du projet▲
Nous voilà prêts à commencer le développement de notre programme Android. La création du projet se fait par l'assistant du plugin Eclipse ADT :
Nous cochons bien « Android 2.0 » comme plateforme cible. Il n'est pas nécessaire de sélectionner « Google APIs ». Ce terme désigne les API développées par Google, par exemple Google Maps, qui ne font pas partie officiellement d'Android ; il n'y a donc aucune garantie que ces bibliothèques soient présentes sur un téléphone même s'il est motorisé par Android.
IV. XMPP, heu c'est quoi ?▲
XMPP signifie eXtensible Messaging and Presence Protocol. Il s'agit d'un protocole standard ouvert de messagerie instantanée anciennement appelé Jabber. XMPP est au cœur de nombreux serveurs de chat, dont GTalk, le service de Google également accessible depuis Gmail.
Si le protocole est avant tout connu pour ses fonctionnalités d'échange de messages textuels et de notification de présence, les possibilités de XMPP sont bien plus larges que ça et peuvent servir de fondations à un véritable MOM (Message-Oriented Middleware).
IV-A. Android et XMPP▲
Le SDK Android n'intègre pas d'API XMPP. Il y a pourtant eu quelques tentatives d'inclusion d'une telle API dans les premières versions du kit de développement. Initialement certaines classes mentionnaient explicitement le nom XMPP, puis à l'occasion des mises à jour successives du SDK, l'API a été renommée « GTalk API » pour être aujourd'hui complètement supprimée.
IV-B. La bibliothèque tierce Smack▲
Android ne nous offrant pas en standard ce qu'il nous faut, nous devons nous tourner vers l'open source, plus précisément vers Smack.
Smack est un projet open source visant à fournir une implémentation Java d'un client XMPP. Ce projet est porté par la société Jive Software qui est très impliquée dans l'évolution de XMPP.
Utiliser Smack dans Android n'est pas si simple que cela ; télécharger le fichier smack.jar et l'intégrer au projet Eclipse ne marche pas ! Eh oui, nous ne devons pas oublier que la machine virtuelle d'Android (Dalvik) n'est pas une JVM standard et seul un sous-ensemble des classes du JDK est implémenté. Quelques petites modifications sont donc nécessaires pour rendre smack compatible avec Dalvik. Un lien vers le jar modifié est disponible à la fin de ce tutoriel.
V. Passons au développement▲
L'interface graphique de notre activité est relativement basique, elle est décrite dans le fichier de définition de layout « main.xml » :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
orientation
=
"vertical"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"fill_parent"
>
<TextView
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"Destinataire :"
/>
<EditText
android
:
id
=
"@+id/recipient"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
android
:
singleLine
=
"true"
android
:
autoText
=
"false"
android
:
capitalize
=
"none"
android
:
scrollHorizontally
=
"true"
/>
<ListView
android
:
id
=
"@+id/thread"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
layout_weight
=
"1"
android
:
scrollbars
=
"horizontal"
/>
<EditText
android
:
id
=
"@+id/message"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
android
:
singleLine
=
"true"
android
:
autoText
=
"false"
android
:
capitalize
=
"none"
android
:
scrollHorizontally
=
"true"
/>
<Button
android
:
id
=
"@+id/send"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"Envoyer"
>
</Button>
</LinearLayout>
Le fichier manifeste de notre application veillera bien à réclamer les droits pour accéder à Internet :
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
package
=
"com.developpez.florentgarin.android"
android
:
versionCode
=
"1"
android
:
versionName
=
"1.0"
>
<application
android
:
icon
=
"@drawable/icon"
android
:
label
=
"@string/app_name"
>
<activity
android
:
name
=
".ClientJabberActivity"
android
:
label
=
"@string/app_name"
>
<intent-filter>
<action
android
:
name
=
"android.intent.action.MAIN"
/>
<category
android
:
name
=
"android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
</application>
<uses-sdk
android
:
minSdkVersion
=
"4"
/>
<uses-permission
android
:
name
=
"android.permission.INTERNET"
/>
</manifest>
Ensuite, la classe de l'activité ClientJabberActivity se présente comme suit :
package
com.developpez.florentgarin.android;
import
java.util.ArrayList;
import
java.util.List;
import
org.jivesoftware.smack.*;
import
org.jivesoftware.smack.filter.*;
import
org.jivesoftware.smack.packet.*;
import
org.jivesoftware.smack.util.StringUtils;
import
android.app.Activity;
import
android.os.Bundle;
import
android.os.Handler;
import
android.view.View;
import
android.widget.*;
public
class
ClientJabberActivity extends
Activity {
private
final
static
String SERVER_HOST =
"talk.google.com"
;
private
final
static
int
SERVER_PORT =
5222
;
private
final
static
String SERVICE_NAME =
"gmail.com"
;
private
final
static
String LOGIN =
"xxxxx.xxxxx@gmail.com"
;
private
final
static
String PASSWORD =
"xxxxx"
;
private
List<
String>
m_discussionThread;
private
ArrayAdapter<
String>
m_discussionThreadAdapter;
private
XMPPConnection m_connection;
private
Handler m_handler;
/** Called when the activity is first created. */
@Override
public
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.main);
m_handler =
new
Handler
(
);
try
{
initConnection
(
);
}
catch
(
XMPPException e) {
e.printStackTrace
(
);
}
final
EditText recipient =
(
EditText) this
.findViewById
(
R.id.recipient);
final
EditText message =
(
EditText) this
.findViewById
(
R.id.message);
ListView list =
(
ListView) this
.findViewById
(
R.id.thread);
m_discussionThread =
new
ArrayList<
String>(
);
m_discussionThreadAdapter =
new
ArrayAdapter<
String>(
this
,
R.layout.multi_line_list_item, m_discussionThread);
list.setAdapter
(
m_discussionThreadAdapter);
Button send =
(
Button) this
.findViewById
(
R.id.send);
send.setOnClickListener
(
new
View.OnClickListener
(
) {
public
void
onClick
(
View view) {
String to =
recipient.getText
(
).toString
(
);
String text =
message.getText
(
).toString
(
);
Message msg =
new
Message
(
to, Message.Type.chat);
msg.setBody
(
text);
m_connection.sendPacket
(
msg);
m_discussionThread.add
(
"moi :"
);
m_discussionThread.add
(
text);
m_discussionThreadAdapter.notifyDataSetChanged
(
);
}
}
);
}
private
void
initConnection
(
) throws
XMPPException {
//Initialisation de la connexion
ConnectionConfiguration config =
new
ConnectionConfiguration
(
SERVER_HOST, SERVER_PORT, SERVICE_NAME);
m_connection =
new
XMPPConnection
(
config);
m_connection.connect
(
);
m_connection.login
(
LOGIN, PASSWORD);
Presence presence =
new
Presence
(
Presence.Type.available);
m_connection.sendPacket
(
presence);
//enregistrement de l'écouteur de messages
PacketFilter filter =
new
MessageTypeFilter
(
Message.Type.chat);
m_connection.addPacketListener
(
new
PacketListener
(
) {
public
void
processPacket
(
Packet packet) {
Message message =
(
Message) packet;
if
(
message.getBody
(
) !=
null
) {
String fromName =
StringUtils.parseBareAddress
(
message
.getFrom
(
));
m_discussionThread.add
(
fromName +
":"
);
m_discussionThread.add
(
message.getBody
(
));
m_handler.post
(
new
Runnable
(
) {
public
void
run
(
) {
m_discussionThreadAdapter.notifyDataSetChanged
(
);
}
}
);
}
}
}
, filter);
}
}
Le fichier « multi_line_list_item.xml » sert à effectuer le rendu des lignes de messages :
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
textStyle
=
"bold"
android
:
singleLine
=
"false"
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"wrap_content"
/>
On peut noter le recours à un objet de type Handler pour s'assurer que l'affichage des messages reçus se fait bien par le thread responsable des modifications d'IHM.
VI. Exécution de l'application▲
Au premier lancement, nous devons créer l'image AVD (Android Virtual Device). Ce premier lancement est d'ailleurs très long, il faut s'armer de patience. La meilleure stratégie est de ne pas fermer la fenêtre de l'émulateur pour accélérer les exécutions suivantes.
Enfin, la récompense est au bout ! Nous pouvons dialoguer depuis Android avec un utilisateur qui lui converse par exemple depuis Gmail (celui-ci doit déjà avoir été invité) :
VII. Conclusion▲
Notre application montre que quelques lignes de code suffisent pour envoyer et recevoir des messages XMPP depuis Android. Ce qui est particulièrement intéressant ici c'est la prise en charge intégrale de la tuyauterie d'échanges de messages par le serveur Jabber. On pourrait imaginer développer ainsi un jeu d'échecs ou de bataille navale assez facilement en s'appuyant sur l'infrastructure XMPP décentralisée constituée de la multitude de serveurs privés ou publics implémentant le fameux protocole.
VIII. Téléchargements▲
- Bibliothèque smack modifiée pour Android : FTP (secours HTTP)
- Projet Eclipse complet : FTP (secours HTTP)