Accueil > Non classé > Android pour l’entreprise – 6 – Oubliez Gson, Jackson rocks my world!

Android pour l’entreprise – 6 – Oubliez Gson, Jackson rocks my world!

Logo YMCACet article est la suite de Android pour l’entreprise – 5 – ZenContact, JSON relax avec Gson. Il appartient à la série Android pour l’entreprise, dont le fil conducteur est la réalisation d’une application Android d’annuaire d’entreprise : YMCA.

Cet article présente un autre framework que Gson dédié à la désérialisation JSON, et bien plus adapté à  la plateforme Android : Jackson.

Mea culpa

Tout d’abord, un peu de contexte : le 12 février dernier a eu lieu l’Android Dev Lab. Cet évènement a permis à la communauté Android parisienne de se rencontrer, et fût une belle réussite. On ne peut que remercier Google pour la distribution de Nexus One à toutes les personnes présentes ;-).

pano-nexus-one

A cette occasion, j’ai découvert Jackson (merci Fabien !). D’après des tests de performance réalisés par l’auteur du framework, Jackson serait le plus performant des frameworks Java de (dé)sérialisation JSON. Des tests de performance où l’auteur est à la fois juge et partie, ça laisse dubitatif…

Pour en avoir le cœur net, j’ai remplacé Gson par Jackson au sein d’une vrai application Android, traitant de gros volumes de données JSON. Le résultat est sans appel : Jackson explose outrageusement Gson en terme de temps de désérialisation, sur Android.

Je tiens donc à revenir sur l’article précédent : oubliez Gson, Jackson roXx !

Gson + Android = GC à la ramasse

Lorsqu’on désérialise du JSON avec Gson sur Android, on remarque rapidement (via les logs) que le Garbage Collector intervient toutes les quelques secondes.

On peut donc en déduire que Gson créé un certain nombre d’objets temporaires lorsqu’il désérialise du JSON, ce qui déclenche le passage du GC.

Et c’est là que le bât blesse : le GC Android est lent. Rien à voir avec ce à quoi la plateforme Java nous avait habitué ces dernières années en terme de performance. La désérialisation est donc entrecoupée d’appels au GC, et prend un temps fou !


1
2
D/dalvikvm( 6458): GC freed 8229 objects / 409400 bytes in 101ms
D/dalvikvm( 6458): GC freed 7562 objects / 198736 bytes in 61ms

DataBinding avec Jackson

Jackson propose trois approches :

  • la Streaming API, dont le fonctionnement est proche de SAX (système d’évènements) : c’est l’approche la plus performante,
  • le TreeModel, similaire à DOM (données représentées sous forme d’arbre composé de nœuds) : c’est l’approche la plus flexible,
  • le DataBinding, qui permet d’établir un mapping entre les POJO Java et l’arbre JSON. C’est l’approche la plus simple à utiliser, et Gson fonctionne sur ce principe.

Le DataBinding avec Jackson est très proche du fonctionnement de Gson. Voici ce que devient l’exemple de l’article précédent en remplaçant Gson par Jackson :


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
class MyEntity {

  public String name;

  public MyEntity(String name) {
    this.name = name;
  }

  public String sing() {
    return "I'm "+name;
  }
}
[...]
MyEntity entity = new MyEntity("really, really bad!");

// Gson gson = new Gson();
ObjectMapper mapper = new ObjectMapper();

// String json = gson.toJson(entity)
String json = mapper.writeValueAsString(entity);

System.out.println(json); // {"name":"really, really bad!"}

// MyEntity entity2 = gson.fromJson(json, MyEntity.class);
MyEntity entity2 = mapper.readValue(json, MyEntity.class);

System.out.println(entity2.sing()); // I'm really, really bad!

Easy, isn’t it ?

Les plus pointilleux d’entre vous auront détecté une légère différence… Diantre, le champ name est public, alors qu’il était auparavant private ! En effet, l’auto détection ne prend pas en compte les champs private. Voici les alternatives qui s’offrent à vous :

  • Annoter un champ private avec @JsonProperty. Cette annotation permet en outre de définir la clé Json du champ, ce qui est une bonne pratique puisque cela met à l’abri d’un refactoring malheureux.
  • Utiliser un champ public. Et l’encapsulation alors ? A vous d’estimer dans quelle mesure votre classe est exposée en terme d’API. Pour citer Josh Bloch (Effective Java) :

    If a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields.

  • Définir des getter/setters (même private), qui seront automatiquement détectés par Jackson. A noter qu’avec l’API Reflection, appeler un accesseur est plus performant qu’accéder directement à un champ.
  • Enfin, si vous tenez absolument à utiliser des champs private non annotés, ça devrait arriver pour Jackson 1.6 (cf #JACKSON-244).

Mise à jour du 5 juin 2010 : le bug a été corrigé dans la release 1.5.0 de Jackson.

Jackson singing at the YMCA

J’ai bien entendu fait évoluer YMCA pour l’occasion, mais je ne vais pas rentrer dans les détails, car le changement est trivial. Notez cependant que l’approche pour manipuler les génériques est identique :


1
2
List<ZenContact> contacts = gson.fromJson(reader, new TypeToken<List<ZenContact>>() {}.getType());
List<ZenContact> contacts = mapper.readValue(reader, new TypeReference<List<ZenContact>>() {});

Comme d’habitude :

Conclusion

Il existe un grand nombre de frameworks JSON/Java, et il est difficile d’estimer par avance leurs avantages et inconvénients. Gson est intéressant car maintenu par les équipe Google, mais Jackson est désormais adopté par de plus en plus de frameworks (par exemple Jersey, ou encore Spring MVC, et donc Grails).

Ah, une dernière chose, le jeu de mot de la fin “Rien d’autre qui m’aille que l’Jackson” (Copyright Bastien Jansen).

Share
  1. Johnc639
    28/04/2014 à 08:00 | #1

    reverse phone lookup cell free I wish to voice my gratitude for your generosity giving support to individuals eeeaedcfcbea

  1. 25/02/2010 à 10:12 | #1