Il y a 3 années · 8 minutes · Android, Mobile

Android : les bibliothèques utiles

Dans cet article vous trouverez une liste de dix bibliothèques utilisées dans certaines applications Android développées à Xebia et qui sont actuellement en production. Nous pensons que l’utilisation de ces bibliothèques doit être étudiée avant et durant un projet afin de gagner du temps, de la lisibilité et de la maintenabilité. L’article est articulé autour de questions récurrentes lors du développement d’une application Android : à chaque question, nous avons fait correspondre une bibliothèque.

Comment consommer facilement une API REST ?

Retrofit

Retrofit permet de transformer une API REST en une interface Java puis de la consommer.

public interface HackerNewsApiService {
  @GET("/v0/item/{id}.json")
  News getNews(@Path("id") String id);
}

Un adapteur permet de générer une implémentation de cette interface.

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint("https://hacker-news.firebaseio.com")
    .build();

HackerNewsApiService service = restAdapter.create(HackerNewsApiService.class);

Puis les méthodes de l’adapteur peuvent être appelées pour générer un appel réseau vers l’API.

News news = service.getNews("8863");

Les annotations permettent de manipuler la requête :

  • définir le type de la requête (GET, POST, PUT, DELETE and HEAD),
  • manipuler l’URL (Path, Query, QueryMap),
  • définir le corp (Body),
  • de construire une requête avec les paramètres encodés ou multi-part,
  • de gérer les en-têtes (Headers).

Les appels peuvent être fait de façon synchrone (blocage de l’exécution) si la méthode retourne un type :

@GET("/v0/item/{id}.json")
News getNews(@Path("id") String id);

Ou asynchrone si la méthode prend en paramètre un Callback.

@GET("/v0/item/{id}.json")
void getNews(@Path("id") String id, Callback<News> callback);

Le callback est exécuté sur le thread principal.

Documentation : Retrofit

Comment simplifier les interactions entre composants  ?

EventBus

EventBus est un bus d’évènement spécialement conçu pour Android. Il facilite le découplage entre les composants d’une application par l’utilisation de messages au lieu des "classiques" callbacks. Il est possible de choisir si l’on souhaite traiter le message sur le thread UI ou sur un thread de background. 

public class SampleActivity extends Activity {
    private static final EventBus BUS = EventBus.getDefault();
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.home_activity);
        
        BUS.register(this);
  BUS.post(new SyncEvent());
        BUS.post(new AsyncEvent());
    }
    
    public void onEventMainThread(SyncEvent syncEvent){
  // TODO s'exécute sur le main thread
    }
 
    public void onEventBackgroundThread(AsyncEvent asyncEvent){
  // TODO s'exécute sur un thread de background
    }


    @Override
    protected void onDestroy() {
        BUS.unregister(this);
        super.onDestroy();
    }
 
    public static class SyncEvent {};
    
    public static class AsyncEvent {};
  
}

Documentation : EventBus

Comment manipuler efficacement des données JSON ?

Jackson

Jackson est un projet OpenSource qui permet de manipuler des données au format JSON efficacement. L’intégration de Jackson dans Retrofit est triviale : retrofit-converters. Au final, pour l’appel retrofit suivant :

public interface HackerNewsApiService {
  @GET("/v0/item/{id}.json")
  News getNews(@Path("id") String id);
}

Le JSON associé est le suivant :

{
    "by": "dhouston",
    "id": 8863,
    "title": "My YC app: Dropbox - Throw away your USB drive"
}

Et la classe java correspondante :

public class News {
  public String by;
  public int id;
  public String title;
}

Documentation : Jackson

Quel client HTTP est compatible avec toutes les version d’Android ?

OkHttp

Un des clients les plus efficaces est OkHttp. Les fonctionnalités principales de cette bibliothèque sont les suivantes :

  • le support SPDY permet à toutes les requêtes de partager le même socket (si le host est le même);
  • un pool de connection permet de réduire le temps de connexion au serveur;
  • les réponses transitent sous la forme de packet gzip et sont cachées automatiquement.

OkHttp est résiliant quand le réseau n’est pas performant. Si les services consommés ont plusieurs adresses IP, OkHttp peut alterner en fonction de la performance de chacune. De plus OkHttp initialise ses nouvelles connections avec les fonctionnalités TLS sinon il utilisera le protocole SSLv3.

Exemple de récupération d’un contenu à partir d’une URL.

OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
 Request req = new Request.Builder()
  .url(url)
  .build();
 Response resp = client.newCall(request).execute();
 return response.body().string();
}

Utiliser OkHttp avec Retrofit

OkHttp peut être utilisé avec Retrofit.

RestAdapter restAdapter = new RestAdapter.Builder()
 .setClient(new OkHttpClient())
    .setEndpoint("https://hacker-news.firebaseio.com")
    .build();

HackerNewsApiService service = restAdapter.create(HackerNewsApiService.class);

Documentation : OkHttp

Comment éviter les nombreux findViewById ?

Butter Knife

Butter Knife est une bibliothèque d’injection de vues. Elle repose sur le principe d’annotation processing afin de générer du code boilerplate pour vous. Fini les findViewById et les inner-classes pour les listeners ! De plus le code généré est facilement debuggable.

Pour injecter une vue, il suffit d’utiliser l’annotation @InjectView  :

public class HomeActivity extends Activity {
  @InjectView(R.id.title) TextView title;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.inject(this);
    title.setText("Hello world!");
  }
}

Pour ajouter un OnClickListener, il suffit d’annoter une méthode avec @OnClick :

public class HomeActivity extends Activity {

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity_with_button);
    ButterKnife.inject(this);
  }
 
  @OnClick public void onButtonClicked(Button button) {
    button.setText("Clicked!");
  }
}

Cette librairie propose beaucoup d’autres annotations : @OnItemSelected, @OnFocusChanged, @OnLongClick… 

Pour finir Butter Knife ne se limite pas qu’aux activités :

public class HomeFragment extends Fragment {
  @InjectView(R.id.title) TextView title;
  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.simple_fragment, container, false);
  }
 
  @Override public void onViewCreated(View view, Bundle savedInstanceState) {
    ButterKnife.inject(this, view);
    title.setText("Hello world!");
  }
}

Documentation : Butter Knife

Comment charger et manipuler des images efficacement ?

Picasso

Picasso permet de gérer le téléchargement et le cache d’image très simplement.

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Le recyclage, l’annulation du téléchargement et le cache sont gérés automatiquement. Les transformations sur les images sont optimisées et plus simple à faire.

Documentation : Picasso

Comment gérer la sauvegarde et la restauration des objets lors d’un changement d’état ?

IcePick

IcePick utilise des annotations pour générer le code qui permet d’ajouter un objet au Bundle puis de le restaurer.

class ExampleActivity extends Activity {
  @Icicle String name; // Sauvegarde et restauration automatique du champ.

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

Documentation : IcePick

Comment écrire les logs sur la sortie standard en développement et sur un service externe en production ?

Timber

Timber est un outils basé sur la classe Log d’Android configurable par environnement à travers une simple API.

La classe Tree permet de définir le comportement des logs.

Par exemple, pour envoyer un log sur Crashlytics :

private static class CrashReportingTree extends Timber.HollowTree {
    @Override
    public void e(String message, Object... args) {
      Crashlytics.log(Log.ERROR, String.format(message, args));
    }

    @Override
    public void e(Throwable t, String message, Object... args) {
      e(message, args);
      Crashlytics.logException(t);
    }
}

Pour commencer à utiliser Timber, il faut appeler la méthode Timber.plant avec une classe Tree en paramètre dans le méthode onCreate() de votre application.

if (BuildConfig.DEBUG) {
 Timber.plant(new DebugTree());
} else {
 Timber.plant(new CrashReportingTree());
}

L’implémentation DebugTree ajoutera automatiquement le nom de la classe et l’utilisera comme tag.

Documentation : Timber

Comment définir efficacement un ContentProvider ?

Schematic

Cette bibliothèque génère automatiquement un ContentProvider à l’aide d’annotations. Le content provider généré utilise une base de données SQLite pour la persistence. 

Il faut d’abord définir les colonnes de la table :

public interface NewsColumns {
 @DataType(INTEGER) @PrimaryKey @AutoIncrement String _ID = "_id";
 @DataType(TEXT) @NotNull String title = "title";
}

Puis créer la base de données associée :

@Database(version = NewsCollectionDatabase.VERSION)
public final class NewsCollectionDatabase {

  public static final int VERSION = 1;

  @Table(NewsColumns.class) public static final String NEWS_COLLECTION = "newsCollection";
}

Pour finir il reste à déclarer le content provider :

@ContentProvider(authority = NewsCollectionProvider.AUTHORITY, database = NewsCollectionDatabase.class)
public final class NewsCollectionProvider {

  public static final String AUTHORITY = "fr.xebia.android.NewsCollectionProvider";

  @TableEndpoint(table = NewsCollectionDatabase.NEWS_COLLECTION) 
  public static class NewsCollection {

    @ContentUri(
        path = Path.NewsCollection,
        type = "vnd.android.cursor.dir/news",
        defaultSort = NewsColumns.TITLE + " ASC")
    public static final Uri LISTS = Uri.parse("content://" + AUTHORITY + "/newsCollection")
  }

Documentation : Schematic

Comment focaliser ses développements sur les classes essentielles de son application ?

Dagger

Dagger est un framework d’injection de dépendances qui s’articule autour de la JSR-330. Pour rappel l’injection de dépendances favorise l’écriture de code modulable, réutilisable et surtout testable ! Contrairement à Guice la vérification des dépendances se fait à la compilation, de plus Dagger utilise très peu de reflexion dans sa version 1.x.

public class HomeActivity extends Activity {
    @Inject HackerNewsApiService hackerNewsApiService;
    
    ObjectGraph objectGraph;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.home_activity);
        objectGraph = ObjectGraph.create(new HomeActivityModule());
        objectGraph.inject(this);
  // TODO use the hacker news service
    }
 
 @Module(injects = HomeActivity.class)
    public static class HomeActivityModule {
        @Singleton public HackerNewsApiService provideHackerNewsApiService(){
            return new RestAdapter.Builder().setEndpoint("https://hacker-news.firebaseio.com")
                                  .build().create(HackerNewsApiService.class);
  }
    }
}

Documentation : Dagger

La liste présentée ci-dessus n’est pas exhaustive et nous nous efforçons de toujours rechercher de nouvelles bibliothèques intéressantes pour nos développements. Si vous pensez à une bibliothèque qui n’est pas citée dans cet article, n’hésitez pas à poster un commentaire.

 

Benjamin Lacroix / https://twitter.com/benjlacroix

Thomas Guerin / https://twitter.com/Tom404_

Benjamin Lacroix
Benjamin Lacroix est lead développeur et manager chez Xebia. Il est également formateur au sein de Xebia Training .
Thomas Guerin
Consultant Java

4 réflexions au sujet de « Android : les bibliothèques utiles »

  1. Publié par Maxime Picque, Il y a 3 années

    Je ne vois pas AndroidAnnotation dans votre liste, c’est pourtant un bon framework simplifiant énormément le dév Android : http://androidannotations.org/

  2. Publié par Benjamin Lacroix, Il y a 3 années

    Nous avons présenté dans l’article des bibliothèques simples à mettre en place et répondant à une question. Android Annotation est plus un framework qui répond à plusieurs questions.

    Merci pour le commentaire.

  3. Publié par Lionel Schinckus, Il y a 3 années

    Personnellement, j’aime bien Google Volley pour les requêtes REST (Gestion des requêtes via un Thread Pool ce qui est pratique). Je suis étonné aussi de ne pas voir GSON, qui est une très bonne bibliothèque pour sérialiser/déserrialiser du JSON.

    Aussi non, quand Retrofit génère-t-il l’implémentation?

  4. Publié par Benjamin Lacroix, Il y a 3 années

    Google Volley est une très bonne bibliothèque que nous avons aussi utilisé. Nous n’avons pas proposé GSON, mais c’est aussi une bonne bibliothèque. D’après quelques benchmark Jackson serait plus rapide… (à prendre avec des pincettes).

    Retrofit repose sur le principe d’Annotation Processing, la bibliothèque fonctionne par réflexion au runtime.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *