Licence CC BY-NC-SA

La connectivité réseau

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Maintenant que vous savez tout ce qu'il y a à savoir sur les différentes facettes des applications Android, voyons maintenant ce que nous offre notre terminal en matière de fonctionnalités. La première sur laquelle nous allons nous pencher est la connectivité réseau, en particulier l'accès à internet. On va ainsi voir comment surveiller la connexion au réseau ainsi que comment contrôler cet accès. Afin de se connecter à internet, le terminal peut utiliser deux interfaces. Soit le réseau mobile (3G, 4G, etc.), soit le WiFi.

Il y a de fortes chances pour que ce chapitre vous soit utile, puisque statistiquement la permission la plus demandée est celle qui permet de se connecter à internet.

Surveiller le réseau

Avant toute chose, nous devons nous assurer que l'appareil a bien accès à internet. Pour cela, nous avons besoin de demander la permission au système dans le Manifest :

1
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Il existe deux classes qui permettent d'obtenir des informations sur l'état du réseau. Si vous voulez des informations sur sa disponibilité de manière générale, utilisez ConnectivityManager. En revanche, si vous souhaitez des informations sur l'état de l'une des interfaces réseau (en général le réseau mobile ou le WiFi), alors utilisez plutôt NetworkInfo.

On peut récupérer le gestionnaire de connexions dans un Context avec ConnectivityManager Context.getSystemService(Context.CONNECTIVITY_SERVICE).

Ensuite, pour savoir quelle est l'interface active, on peut utiliser la méthode NetworkInfo getActiveNetworkInfo(). Si aucun réseau n'est disponible, cette méthode renverra null.

Vous pouvez aussi vérifier l'état de chaque interface avec NetworkInfo getNetworkInfo(ConnectivityManager.TYPE_WIFI) ou NetworkInfo getNetworkInfo(ConnectivityManager.TYPE_MOBILE) pour le réseau mobile.

Enfin, il est possible de demander à un NetworkInfo s'il est connecté à l'aide de la méthode boolean isAvailable() :

1
2
3
4
5
6
7
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

if(networkInfo != null && networkInfo.isAvailable() && networkInfo.isConnected()) {
  boolean wifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
  Log.d("NetworkState", "L'interface de connexion active est du Wifi : " + wifi);
}

De manière générale, on préférera utiliser internet si l'utilisateur est en WiFi parce que le réseau mobile est plus lent et est souvent payant. Il est conseillé de mettre en garde l'utilisateur avant de télécharger quelque chose en réseau mobile. Vous pouvez aussi envisager de bloquer les téléchargements quand seul le réseau mobile est disponible, comme c'est souvent fait.

Il est cependant possible que l'état de la connexion change et qu'il vous faille réagir à ce changement. Dès qu'un changement surgit, le broadcast intent ConnectivityManager.CONNECTIVITY_ACTION est envoyé (sa valeur est étrangement android.net.conn.CONNECTIVITY_CHANGE). Vous pourrez donc l'écouter avec un receiver déclaré de cette manière :

1
2
3
4
5
<receiver android:name=".ConnectionChangesReceiver" >
  <intent-filter >
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
  </intent-filter>
</receiver>

Vous trouverez ensuite dans les extras de l'intent plus d'informations. Par exemple ConnectivityManager.EXTRA_NO_CONNECTIVITY renvoie un booléen qui vaut true s'il n'y a pas de connexion à internet en cours. Vous pouvez aussi obtenir directement un NetworkInfo avec l'extra ConnectivityManager.EXTRA_OTHER_NETWORK_INFO afin d'avoir plus d'informations sur le changement.

Afficher des pages Web

Il pourrait vous prendre l'envie de montrer à votre utilisateur une page Web. Ou alors il se peut que vous vouliez faire une interface graphique à l'aide de HTML. Nous avons déjà vu une méthode pour mettre du HTML dans des TextView, mais ces méthodes ne sont pas valides pour des utilisations plus poussées du HTML, comme par exemple pour afficher des images ; alors pour afficher une page complète, n'imaginez même pas.

Ainsi, pour avoir une utilisation plus poussée de HTML, on va utiliser une nouvelle vue qui s'appelle WebView. En plus d'être une vue très puissante, WebView est commandé par WebKit, un moteur de rendu de page Web qui fournit des méthodes pratiques pour récupérer des pages sur internet, effectuer des recherches dans la page, etc.

Charger directement du HTML

Pour insérer des données HTML sous forme textuelle, vous pouvez utiliser void loadData(String data, String mimeType, String encoding) avec data les données HTML, mimeType le type MIME (en général text/html) et l'encodage défini dans encoding. Si vous ne savez pas quoi mettre pour encoding, mettez « UTF-8 », cela devrait aller la plupart du temps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

public class WebViewActivity extends Activity {
  private WebView mWebView = null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_web_view);

    mWebView = (WebView) findViewById(R.id.webview);
    mWebView.loadData("<html><head><meta charset=\"utf-8\" /></head>" + "<body>Salut les Zéros !</body></html>", "text/html", "UTF-8");
  }

}

On obtient alors la figure suivante.

Du HTML s'affiche

N'oubliez pas de préciser l'encodage dans l'en-tête, sinon vos accents ne passeront pas.

Charger une page sur internet

La première chose à faire est de demander la permission pour aller sur internet dans le Manifest :

1
<uses-permission android:name="android.permission.INTERNET" />

Puis vous pouvez charger le contenu avec void loadUrl(String url). Ainsi, avec ce code :

1
2
3
4
5
6
7
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_web_view);

  mWebView = (WebView) findViewById(R.id.webview);
  mWebView.loadUrl("http://www.siteduzero.com");
}

… on obtient la figure suivante :

Le Site du Zéro est affiché à l'écran

Effectuer des requêtes HTTP

Rappels sur le protocole HTTP

HTTP est un protocole de communication, c'est-à-dire un ensemble de règles à suivre quand deux machines veulent communiquer. On l'utilise surtout dans le cadre du World Wide Web, une des applications d'internet, celle qui vous permet de voir des sites en ligne. Vous remarquerez d'ailleurs que l'URI que vous utilisez pour accéder à un site sur internet a pour schéma http:, comme sur cette adresse : http://www.siteduzero.com.

Il fonctionne de cette manière : un client envoie une requête HTTP à un serveur qui va réagir et répondre en HTTP en fonction de cette entrée, comme le montre la figure suivante.

La requête et la réponse utilisent le même protocole, mais leur contenu est déterminé par le client ou le serveur

Il existe plusieurs méthodes en HTTP. Ce sont des commandes, des ordres qui accompagnent les requêtes. Par exemple, si on veut récupérer une ressource on utilise la méthode GET. Quand vous tapez une adresse dans la barre de navigation de votre navigateur internet, il fera un GET pour récupérer le contenu de la page.

À l'opposé de GET, on trouve POST qui est utilisé pour envoyer des informations. Quand vous vous inscrivez sur un site, ce qui se fait souvent à l'aide d'un formulaire, l'envoi de ce dernier correspond en fait à un POST vers le serveur qui contient les diverses informations que vous avez envoyées. Mais comment ça fonctionne, concrètement ? C'est simple, dans votre requête POST, votre navigateur va ajouter comme données un ensemble de couples identifiant-clé de cette forme-ci : identifiant1=clé1&identifiant2=clé2&identifiant3=clé3. Ainsi, votre serveur sera capable de retrouver les identifiants avec les clés qui y sont associées.

Le HTTP sous Android

Il existe deux méthodes pour manipuler le protocole HTTP sous Android. La première est fournie par Apache et est celle que vous êtes censés utiliser avant l'API 9 (Gingerbread). En pratique, nous allons voir l'autre méthode même si elle n'est pas recommandée pour l'API 7, parce qu'elle est à privilégier pour la plupart de vos applications.

Pour commencer, la première chose à faire est de savoir sur quelle URL on va opérer avec un objet de type URL. La manière la plus simple d'en créer un est de le faire à l'aide d'une chaîne de caractères :

1
URL sdz = new URL("http://www.siteduzero.com")

Toutes vos requêtes HTTP devront se faire dans un thread différent du thread UI, puisqu'il s'agit de processus lents qui risqueraient d'affecter les performances de votre application.

On peut ensuite ouvrir une connexion vers cette URL avec la méthode URLConnection openConnection(). Elle renvoie un URLConnection, qui est une classe permettant de lire et d'écrire depuis une URL. Ici, nous allons voir en particulier la connexion à une URL avec le protocole HTTP, on va donc utiliser une classe qui dérive de URLConnection : HttpURLConnection.

1
2
URLConnection urlConnection = url.openConnection();
HttpURLConnection httpUrlConnection = (HttpURLConnection)connection;

Il est ensuite possible de récupérer le flux afin de lire des données en utilisant la méthode InputStream getInputStream(). Avant cela, vous souhaiterez peut être vérifier le code de réponse fourni par le serveur HTTP, car votre application ne réagira pas de la même manière si vous recevez une erreur ou si tout s'est déroulé correctement. Vous pouvez le faire avec la méthode int getResponseCode() :

1
2
3
4
5
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
  InputStream stream = httpConnection.getInputStream();
  // Par exemple…
  stream.read();
}

Enfin, si vous voulez effectuer des requêtes sortantes, c'est-à-dire vers un serveur, il faudra utiliser la méthode setDoOutput(true) sur votre HttpURLConnection afin d'autoriser les flux sortants. Ensuite, si vous connaissez la taille des paquets que vous allez transmettre, utilisez void setFixedLengthStreamingMode(int contentLength) pour optimiser la procédure, avec contentLength la taille des paquets. En revanche, si vous ne connaissez pas cette taille, alors utilisez setChunkedStreamingMode(0) qui va séparer votre requête en paquets d'une taille définie par le système :

1
2
3
4
5
6
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);

OutputStream stream = new BufferedOutputStream(urlConnection.getOutputStream());
writeStream(stream );

Dans les versions les plus récentes d'Android, effectuer des requêtes HTTP dans le thread UI soulèvera une exception, vous serez donc obligés de créer un thread pour effectuer vos requêtes. De toute manière, même si vous êtes dans une ancienne version qui ne soulève pas d'exception, il vous faut quand même créer un nouveau thread, parce que c'est la bonne manière.

Pour finir, comme pour n'importe quel autre flux, n'oubliez pas de vous déconnecter avec void disconnect().

Avant de vous laissez, je vais vous montrer une utilisation correcte de cette classe. Vous vous rappelez que je vous avais dit que normalement il ne fallait pas utiliser cette API pour faire ces requêtes ; c'est en fait parce qu'elle est boguée. L'un des bugs qui vous agacera le plus est que, vous aurez beau demander de fermer un flux, Android ne le fera pas. Pour passer outre, nous allons désactiver une fonctionnalité du système qui permet de contourner le problème :

1
System.setProperty("http.keepAlive", "false");

Voici par exemple une petite application qui envoie des données à une adresse et récupère ensuite la réponse :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
System.setProperty("http.keepAlive", "false");
OutputStreamWriter writer = null;
BufferedReader reader = null;
URLConnection connexion = null;
try {
  // Encodage des paramètres de la requête
  String donnees = URLEncoder.encode("identifiant1", "UTF-8")+ "="+URLEncoder.encode("valeur1", "UTF-8");
  donnees += "&"+URLEncoder.encode("identifiant2", "UTF-8")+ "=" + URLEncoder.encode("valeur2", "UTF-8");

  // On a envoyé les données à une adresse distante
  URL url = new URL(adresse);
  connexion = url.openConnection();
  connexion.setDoOutput(true);
  connexion.setChunkedStreamingMode(0);

  // On envoie la requête ici
  writer = new OutputStreamWriter(connexion.getOutputStream());

  // On insère les données dans notre flux
  writer.write(donnees);

  // Et on s'assure que le flux est vidé
  writer.flush();

  // On lit la réponse ici
  reader = new BufferedReader(new InputStreamReader(connexion.getInputStream()));
  String ligne;

  // Tant que « ligne » n'est pas null, c'est que le flux n'a pas terminé d'envoyer des informations
  while ((ligne = reader.readLine()) != null) {
    System.out.println(ligne);
  }
} catch (Exception e) {
  e.printStackTrace();
} finally {
  try{writer.close();}catch(Exception e){}
  try{reader.close();}catch(Exception e){}
  try{connexion.disconnect();}catch(Exception e){}
}

  • Dans votre vie de programmeur Android, il est très probable que vous liez au moins une application à internet tellement c'est quelque chose de courant.
  • On peut obtenir des informations sur l'état de la connectivité de l'appareil grâce à ConnectivityManager. C'est indispensable parce qu'il y a des chances que l'utilisateur passe du Wifi à un réseau mobile lorsqu'il exploite votre application. Dès que ça arrive, il faut couper tout téléchargement pour que votre pauvre utilisateur ne se retrouve pas avec une facture longue comme son bras !
  • On peut très facilement afficher du code HTML avec une WebView.
  • Sur le web, pour envoyer et recevoir des applications, on utilise le protocole HTTP. Il possède en autre la méthode GET pour récupérer du contenu sur internet et la méthode POST pour en envoyer.
  • Quand on récupère du contenu sur internet, on passe toujours par un thread, car récupérer cela prend du temps et impacterait trop l'utilisateur si on l'employait dans le thread UI.
  • On récupère et on poste du contenu facilement à l'aide de flux comme nous l'avons toujours fait pour écrire dans un fichier.