Mobile

Utilisation de Dagger (injection de dépendances) dans une application Android

Publié le : Auteur: Ronan Bernabé Laisser un commentaire
mobile

L’utilité des moteurs d’injection de dépendances n’est plus à démontrer. Cela permet, entre autres, d’écrire un code plus concis, de réduire les dépendances et cela peut faciliter l’utilisation d’implémentations alternatives notamment pour la réalisation des tests.

Nativement, le SDK Android n’inclut pas de moteur d’injection de dépendances et les librairies fournissant un moteur d’injection de dépendances ne sont pas nombreuses. Roboguice et Dagger semblent les principales. Cet article présente une mise en œuvre du mécanisme d’injection de dépendances (DI) avec Dagger dans une application Android.

Mise en garde

Avant d’intégrer une librairie d’injection de dépendances dans votre projet Android, sachez que de telles librairies ne sont pas toujours neutres sur les performances de votre application. Google déconseille même l’usage de librairies d’injection de dépendances car elles peuvent occasionner une surconsommation de ressources. En effet des objets supplémentaires seront chargés en mémoire, des traitements supplémentaires d’inspection seront réalisés.

Dagger par l’exemple

Néanmoins, Dagger semble (je n’ai pas réalisé de benchmark et me fie aux retours d’expériences présents sur la Toile) une librairie assez légère pour permettre de s’attarder dessus. Nous allons donc nous attacher à injecter l’implémentation d’un service dans une Activity Android. Bien entendu, la classe d’implémentation du service ne doit pas être connue de l’Activity, seule son interface doit l’être. C’est parti…

Notre service et son interface

public interface IDummySvc {
  String getPhrase();
}
public class DummySvc implements IDummySvc {
  @Override
  public String getPhrase() {
	return "Hello World !";
  }
}

Inclusion des librairies Dagger

Maintenant, il faut inclure les librairies de Dagger :

  • Ajouter dagger-x.x.x.jar aux librairies de votre projet
  • Ajouter aussi la dépendance javax.inject-1.jar qui n’est pas fournie
  • Activer le processeur d’annotations de votre IDE et indiquer lui les librairies suivantes
    • dagger-x.x.x.jar
    • javax.inject-1.jar
    • dagger-compiler-x.x.x.jar, Dagger génère des classes en s’appuyant sur javawriter
    • javawriter-2.2.1.jar (attention, pas sûr que les versions supérieures de cette librairie soient compatibles)

Les fournisseurs (ou modules)

Pour obtenir les implémentations, Dagger propose la création de modules (Factory) rendant ce service. Notez l’utilisation des annotations @Module et @Provide.

@Module
public class DummyModule {
  @Provides
  public IDummySvc providesDummySvc() {
    return new DummySvc();
  }
}

Injection du service dans l’Activity

public class MainActivity extends Activity {
 
  @Inject
  IDummySvc dummySvc;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
 
	((TextView)this.findViewById(R.id.txtHello)).setText("Hello " + dummySvc.getName());
  }
}

Construction du graphe

Ce n’est pas fini. Dagger repose sur la construction d’un graphe d’objets fédérant les connaissances utiles aux injections. Le plus simple est donc d’initialiser ce graphe au niveau de l’application. On créer donc une application si nécessaire :

public class TestApp extends Application {
 
  private ObjectGraph graph;
 
  @Override
  public void onCreate() {
	super.onCreate();
	graph = ObjectGraph.create(new DummyModule());
  }
 
}

Ajouter les instructions d’initialisation

Et ce n’est pas fini ! (C’est là que Dagger devient un peu lourd à coder, espérons pour plus de légèreté en mémoire). Il faut ajouter des instructions pour compléter le graphe de connaissance :

  • Au niveau du module avec l’attribut inject permettant d’indiquer les consommateurs :
@Module(injects = MainActivity.class)
public class DummyModule {
  @Provides
  public IDummySvc providesDummySvc() {
    return new DummySvc();
  }
}
  • Et au niveau de l’Activity, en injectant directement cette dernière dans le graphe :
public class MainActivity extends Activity {
 
  @Inject
  IDummySvc dummySvc;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
 
	((TestApp)getApplication()).inject(this);
 
	((TextView)this.findViewById(R.id.txtHello)).setText("Hello " + dummySvc.getName());
  }
}

public class TestApp extends Application {
 
  private ObjectGraph graph;
 
  @Override
  public void onCreate() {
	super.onCreate();
	graph = ObjectGraph.create(new DummyModule());
  }
 
  public void inject(Object obj) {
	graph.inject(obj);
  }
 
}

Conclusion

Le contrat est donc rempli pour Dagger qui permet facilement de bénéficier de l’injection de dépendances sous Android.

On apprécie tout particulièrement les validations que Dagger effectue à la compilation.

En revanche, il est dommage de devoir injecter les utilisateurs (l’Activity dans notre exemple) dans le graphe Dagger ET de devoir déclarer ces utilisateurs au niveau des modules. Cela semble faire doublon, et surtout parait peu élégant car est alors définie une dépendance entre les modules (fournisseurs) et les utilisateurs.