• Aucun résultat trouvé

1.17.4 L'interface [IDao] L'interface [IDao] devient la suivante :

1. package exemples.android.dao; 2.

3. import rx.Observable; 4.

5. publicinterface IDao { 6.

7. // nombre aléatoire

8. Observable<Integer> getAlea(int a, int b); 9.

10. // URL du service web

11. void setUrlServiceWebJson(String url); 12.

13. // délai d'attente (ms) max de la réponse du serveur

14. void setTimeout(int timeout); 15.

16. // délai d'attente en millisecondes du client avant requête

17. void setDelay(int delay); 18.

19. // mode debug

20. void setDebugMode(boolean isDebugEnabled); 21. }

• ligne 8 : la méthode [getAlea] rend désormais un type [Observable] de la bibliothèque RxJava (ligne 3). Le principe est le

suivant :

Un flux d'éléments de type Observable<T> est observé par un ou plusieurs souscripteurs (abonnés, observateurs, consommateurs) de type Subscriber<T>. La bibliothèque RxJava permet que le flux Observable<T> s'exécute dans un thread T1 et son observateur Subscriber<T> dans un thread T2 sans que le développeur n'ait à se soucier de gérer le cycle de vie de ces threads et de problèmes naturellement difficiles, tels que le partage de données entre threads et la synchronisation de ceux-ci pour exécuter une tâche globale. Elle facilite donc la programmation asynchrone.

1.17.5 La classe [AbstractDao]

Nous ferons dériver la classe [Dao] de la classe [AbstractDao] suivante : 1. package exemples.android.dao; 2. 3. import com.fasterxml.jackson.core.JsonProcessingException; 4. import com.fasterxml.jackson.databind.ObjectMapper; 5. import rx.Observable; 6. import rx.Subscriber; 7.

8. publicabstract class AbstractDao { 9.

10. // mappeur jSON

11. private ObjectMapper mapper = new ObjectMapper(); 12.

13. // méthodes protégées ---

14. // interface générique

15. protectedinterface IRequest<T> { 16. Response<T> getResponse(); 17. }

18.

19. // requête générique

20. protected <T> Observable<T> getResponse(final IRequest<T> request) { 21. // exécution service

22. return rx.Observable.create(new rx.Observable.OnSubscribe<T>() { 23. @Override

24. publicvoid call(Subscriber<? super T> subscriber) { 25. DaoException ex = null;

26. // exécution service

27. try {

29. Response<T> response = request.getResponse(); 30. // erreur ?

31. int status = response.getStatus(); 32. if (status != 0) {

33. // on note l'exception

34. ex = new DaoException(mapper.writeValueAsString(response.getMessages()), status); 35. } else {

36. // on émet la réponse

37. subscriber.onNext(response.getBody()); 38. // on signale la fin de l'observable

39. subscriber.onCompleted(); 40. }

41. } catch (JsonProcessingException | RuntimeException e) { 42. // on note l'exception 43. ex = new DaoException(e, 100); 44. } 45. // exception ? 46. if (ex != null) { 47. // on émet l'exception 48. subscriber.onError(ex); 49. } 50. } 51. }); 52. } 53. 54. }

• la classe [AbstractDao] a comme principal élément une méthode générique [getResponse] qui sert à obtenir du serveur un

type [Response<T>] où T est le type du résultat désiré par le client HTTP (ici Integer) ;

• ligne 20 : l'unique paramètre de la méthode générique [getResponse] est une instance de l'interface générique [IRequest<T>] des lignes 15-17. Celle-ci n'a qu'une méthode [getResponse] et c'est cette méthode qui fournit la réponse [Response<T>] désirée ;

• grâce aux deux éléments précédents, la classe [AbstractDao] peut servir de classe parent à toute couche [Dao] cliente d'un

serveur envoyant des réponses de type [Response<T>] ;

• ligne 20 : la méthode générique [getResponse] rend un type [Observable<T>] qui représente le résultat réellement attendu

par le client HTTP (ici un type Observable<Integer>) ;

• lignes 22-51 : la méthode statique [rx.Observable.create] crée un type [Observable] ;

• ligne 22 : l'unique paramètre de cette méthode est une instance du type [rx.Observable.OnSubscribe<T>], une interface

qui possède les méthodes suivantes :

◦ [onNext(T element)] : permet d'émettre vers un observateur un élément de type T ;

◦ [onError(Throwable th)] : permet d'émettre vers un observateur une exception ;

◦ [onCompleted] : permet d'indiquer à un observateur la fin des émissions ;

Un type [Observable<T>] obéit à certaines contraintes :

• il émet ses éléments avec la méthode [onNext(T element)] ;

• la méthode [onCompleted] doit être appelée une unique fois dès qu'il n'y a plus d'éléments à émettre vers

l'observateur ;

• la méthode [onCompleted] n'est pas appelée si la méthode [onError(Throwable th)] l'a été ;

Dans notre exemple :

• l'observateur sera le fragment [Vue1Fragment]. C'est lui qui consomme les éléments émis par le [Observable<T>] (élément ou exception) ;

• le type [Observable<T>] créé n'émettra qu'un unique élément (ligne 37) ;

ligne 29 : fait une requête HTTP synchrone au serveur et obtient le type [Response<T>]. Cette requête HTTP est assurée

par le type [IRequest] passé en paramètre à la méthode générique [getResponse] ; • ligne 31 : on récupère le status de la réponse ;

lignes 32-34 : si ce status est celui d'une erreur, on prépare une exception ;

lignes 36-39 : si ce status n'est pas celui d'une erreur, alors on émet la réponse attendue réellement par le client (ligne 37) et on indique à l'observateur qu'il n'y aura plus d'autres émissions (ligne 39) ;

• lignes 41-44 : si la requête HTTP se termine par une exception, on note celle-ci ;

lignes 46-49 : si l'exception [ex] est différente de null, alors on l'émet vers l'observateur. Il n'y a là pas lieu d'appeler la méthode [onCompleted] pour indiquer à l'observateur qu'il n'y aura plus d'émissions d'éléments. C'est implicite ;

On retiendra de ces explications que :

• la méthode générique [<T> Observable<T> getResponse(final IRequest<T> request)] rend un type [Observable<T>] qui n'émet qu'un élément de type T ou bien une exception ;

1.17.6 La classe [Dao]

La classe [Dao] évolue de la façon suivante : 1. @EBean

2. publicclass Dao extends AbstractDao implements IDao { 3.

4. // client du service REST

5. @RestService

6. protected WebClient webClient; 7.

8. // délay d'attente avant exécution requête

9. private int delay; 10. // mode debug

11. private boolean isDebugEnabled; 12. // nom de la classe

13. private String className; 14. 15. // constructeur 16. public Dao() { 17. // nomde la classe 18. className = getClass().getSimpleName(); 19. } 20. 21. 22. // interface IDao --- 23. @Override

24. public Observable<Integer> getAlea(finalint a, finalint b) { 25. // log

26. if (isDebugEnabled) {

27. Log.d(String.format("%s", className), String.format("getAlea [%s, %s] en cours", a, b)); 28. }

29. // exécution client web

30. return getResponse(new IRequest<Integer>() { 31. @Override

32. public Response<Integer> getResponse() { 33. // attente 34. waitSomeTime(delay); 35. // appel HTTP synchrone 36. return webClient.getAlea(a, b); 37. } 38. }); 39. } 40. ...

• ligne 2 : la classe [Dao] étend la classe [AbstractDao] ;

• ligne 24 : la méthode [getAlea] rend désormais un type [Observable<Integer>] ;

• ligne 30 : appel de la méthode générique [getResponse] de la classe parent. On lui passe un paramètre de type [IRequest<Integer>] ;

• lignes 32-37 : implémentation de l'interface [IRequest<Integer>] ;

• ligne 36 : on fait la requête HTTP via l'interface AA [webClient] comme il avait été fait précédemment. On sait qu'on va

récupérer un type [Response<Integer>] qui est bien le type que doit rendre la méthode [IRequest<Integer>.getReponse()] ;

ligne 36 : on utilise ici une propriété appelée closure : la capacité d'encapsuler dans une instance des valeurs extérieures à

celle-ci lorsqu'elle est créée, ici les valeurs de [a, b] de la ligne 24. C'est ce qui permet à la méthode [IRequest<Integer>.getReponse()] de ne pas avoir de paramètres. Ceux-ci ont été gravés dans le corps de la méthode. Et là où normalement on changerait les paramètres de la méthode (a,b) -> (x,y), ici on crée une nouvelle instance de [IRequest<Integer>] encapsulant les valeurs de x et y ;

1.17.7 La classe [MainActivity]

La classe [MainActivity] qui implémente l'interface [IDao] évolue comme suit :

1. // implémentation IDao ---

2.

3. @Override

4. public Observable<Integer> getAlea(int a, int b) { 5. // exécution

6. return dao.getAlea(a, b); 7. }

1.17.8 La classe [Vue1Fragment]