IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

nicroman

[Actualité] Android - Communication avec un webservice - Partie 2

Noter ce billet
par , 02/03/2015 à 18h47 (1256 Affichages)
Nous avons vu dans le billet précédent comment afficher les données... nous allons voir maintenant comment récupérer celles-ci depuis le webservice.

Dans notre cas, il s'agit donc de modifier (et uniquement elle) la fonction "populateListe".

Etape #1 - Une opération longue.
Récupérer la liste des pays et leur population est une opération potentiellement longue. Elle ne peut donc pas être réalisé dans le même "thread" que celui qui gère l'interface (celui du framework par défaut).
Il est donc formellement interdit de populer les données directement depuis la fonction "populateList"

Nous allons donc passer par une AsyncTask.
L'AsyncTask offre tous les outils pour réaliser une opération asynchrone sans trop s'arracher les cheveux, tant sur la gestion des erreurs que sur celle des threads. La classe a aussi d'autres avantages d'optimisations concernant les threads, mais ce n'est pas le sujet ici.

Code java : Sélectionner tout - Visualiser dans une fenêtre à part
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
40
41
 
....
 
    private void populateListe()
    {
         ListePaysTask task = new ListePaysTask();
         task.execute(); // on démarre la tâche asynchrone
    }
 
    // a noter que la tâche aura besoin de communiquer ses résultats à l'activité, elle ne sera donc pas "statique".
    // il y a d'autres solutions pour pouvoir néanmoins créer des AsyncTask statiques (notamment à base de listener), mais encore une fois
    // ce n'est pas le but de ces deux petits articles
    private class ListePaysTask extends AsyncTask<Void,Void,List<Pays>> 
    {
          // la fonction qui sera effectuée en "background"
          @Override
          protected List<Pays> doInBackground(Void ... params)
          {
                ArrayList<Pays>  data = new ArrayList<Pays>();
                // pour cette étape nous conservons les "mock-data"
               data.add(new Pays("Test 1",45789999));
               data.add(new Pays("Test 2",0));
               data.add(new Pays("Test 3",147));
               data.add(new Pays("Test 4",999999999));
               return data;
         }
 
         // la fonction qui sera appelée avec les données retournées par "doInBackground" dans le thread principal
         protected void onPostExecute(List<Pays> data)
         {
              if (data != null) {
// d'ou l’intérêt de séparer les fonctions de récupération de données des fonctions de mise à jour de l'interface....
                   ListePopulationActivity.this.updateListe(data);
              } else {
                   // une erreur s'est produite
                   Toast.makeText(ListePopulationActivity.this,"Erreur de récupération des données",Toast.LENGTH_LONG).show();
              }
         }
     }
 
...

Voilà... à présent notre activité n'a pas changé d'un iota, mais l'ensemble des données est populée en asynchrone.

Etape #2 - Lecture depuis le webservice.
Nous pouvons donc remplacer les données de test par le véritable appel au web-service.
Seule la classe ListePaysTask (et sa fonction doInBackground) seront donc modifiées:

Code java : Sélectionner tout - Visualiser dans une fenêtre à part
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
40
41
42
43
44
45
 
....
      ....
 
      @Override
      protected List<Pays> doInBackground()
      {
            try {
ArrayList<Pays> data = new ArrayList<Pays>();
// Etape 1: on récupère les données sous forme de String
                String strData = getStringResult("http://un.example.com/population.php");
                // Etape 2: on traduit en JSON
                JSONObject jsonData = new JSONObject(strData);
                // Etape 3: on parse le JSON 
                parseListePays(jsonData,data);
return data;
            } catch (Exception ex) {
                Log.e("ListPaysTask","Erreur de récupération des données !",ex);
return null;
            }
}
 
      // on part du principe que l'objet contient une array "pays" contenant la liste de tous les pays sous forme d'objet "name" et "population"
      // throws JSONException sert à indiquer que la fonction risque d'échouer avec cette exception
      private void parseListePays(JSONObject data, ArrayList<Pays> res) throws JSONException
      {
           JSONArray jsonArray = data.getJSONArray("pays");
           for (int i = 0; (i < jsonArray.length()); ++i) {
                JSONObject jsonPays = jsonArray.getJSONObject(i);
                Pays pays = new Pays();
                parsePays(jsonPays,pays);
                res.add(pays);
           }
       }
 
       // encore une fois on sépare la fonction, toute modification de "Pays" ne nécessitera alors que la modification de cette fonction-ci.
       // a noter que dans l'idéal, la classe "Pays" pourrait fournir elle-même la fonction de parsing depuis un objet JSON.
       private void parsePays(JSONObject data, Pays res) throws JSONException
       {
             res.setName(data.getString("name"));
             res.setPopulation(data.getLong("population"));
       }
 
      ...
...

Pourquoi faire trois étapes et pas une seule et grosse fonction ? Tout simplement parce qu'on aime en programmation séparer chaque "niveau":
Le web-service nous renvoie une chaine de caractères => fonction getStringResult
Cette chaine est un objet JSON qui contient une liste de pays => fonction parseListPays()
Chaque élément de cette liste est un objet JSON qui correspond à un Pays => fonction parsePays()

Pour tester, la fonction getStringResult peut renvoyer une chaine (sans appel au webserivce) telle qu'elle est attendue...
Par exemple:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
        private String getStringResult(String url) throws ParseException, IOException {
            return "{\"pays\":[{\"name\":\"Test1\",\"population\":45789999},{\"name\":\"Test 2\",\"population\":0}]}";
        }
Ensuite l'unique try/catch... Il est évident que si une fonction échoue son seul moyen de retourner cet état de fait est en passant par des exceptions... Hors si l'étape-1 échoue, les étapes suivantes ne peuvent être accomplies. Toute les opérations seront donc protégées avec un seul et unique try/catch. Et l'exception sera loguée de manière correcte, pour éviter de sombrer dans l'oubli.

Il reste donc une unique fonction à implémenter, l'appel au webservice lui-même, et pour celle-ci, rien de plus simple.
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
....
    ....
 
// a noter le "throws" des exceptions, car cette fonction *peut* échouer !
         private String getStringResult(String url) throws ParseException, IOException 
         {
              HttpClient client = AndroidHttpClient.newInstance("pays-population v1.0"); // on passe un "user-agent" cohérent.
 
              HttpGet getRequest = new HttpGet(url); // le "get" lui-même est assez simple.
              HttpResponse response = client.execute(getRequest);  // la réponse est obtenue facilement
              HttpEntity entity = response.getEntity();  // le contenu est accessible directement
 
              return EntityUtils.toString(entity,"UTF-8");  // la lecture sous forme de texte, en utilisant les classes outils. 
              //"UTF-8" signifie que *si* la réponse ne contient pas d'information quant à l'encodage, celui-ci sera considéré comme étant en UTF-8
         }
     ....
....


Et voilà, notre application est désormais finie, et fonctionnelle.

Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Viadeo Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Twitter Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Google Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Facebook Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Digg Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Delicious Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog MySpace Envoyer le billet « Android - Communication avec un webservice - Partie 2 » dans le blog Yahoo

Catégories
Java , Android

Commentaires