Comment utiliser le poignard? Comment configurer Dagger pour qu'il fonctionne dans mon projet Android?
Je voudrais utiliser Dagger dans mon Android projet, mais je le trouve déroutant.
EDIT: Dagger2 est également disponible depuis le 15.04.1515, et c'est encore plus déroutant!
[Cette question est un "bout" sur lequel j'ajoute à ma réponse à mesure que j'en apprenais plus sur Dagger1 et que j'en apprenais plus sur Dagger2. Cette question est plus un guide plutôt qu'une "question".]
Guide pour Dagger 2.x (Édition révisée 6):
Les étapes sont les suivantes:
1.) ajoutez Dagger
à vos fichiers _build.gradle
_:
.
_// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.Android.tools.build:gradle:2.2.0'
classpath 'com.neenbedankt.gradle.plugins:Android-apt:1.8' //added apt for source code generation
}
}
allprojects {
repositories {
jcenter()
}
}
_
.
_apply plugin: 'com.Android.application'
apply plugin: 'com.neenbedankt.Android-apt' //needed for source code generation
Android {
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "your.app.id"
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
apt 'com.google.dagger:dagger-compiler:2.7' //needed for source code generation
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.Android.support:appcompat-v7:24.2.1'
compile 'com.google.dagger:dagger:2.7' //dagger itself
provided 'org.glassfish:javax.annotation:10.0-b28' //needed to resolve compilation errors, thanks to tutplus.org for finding the dependency
}
_
2.) Créez votre classe AppContextModule
fournissant les dépendances.
_@Module //a module could also include other modules
public class AppContextModule {
private final CustomApplication application;
public AppContextModule(CustomApplication application) {
this.application = application;
}
@Provides
public CustomApplication application() {
return this.application;
}
@Provides
public Context applicationContext() {
return this.application;
}
@Provides
public LocationManager locationService(Context context) {
return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
}
_
3.) crée la classe AppContextComponent
qui fournit l'interface pour obtenir les classes injectables.
_public interface AppContextComponent {
CustomApplication application(); //provision method
Context applicationContext(); //provision method
LocationManager locationManager(); //provision method
}
_
3.1.) Voici comment créer un module avec une implémentation:
_@Module //this is to show that you can include modules to one another
public class AnotherModule {
@Provides
@Singleton
public AnotherClass anotherClass() {
return new AnotherClassImpl();
}
}
@Module(includes=AnotherModule.class) //this is to show that you can include modules to one another
public class OtherModule {
@Provides
@Singleton
public OtherClass otherClass(AnotherClass anotherClass) {
return new OtherClassImpl(anotherClass);
}
}
public interface AnotherComponent {
AnotherClass anotherClass();
}
public interface OtherComponent extends AnotherComponent {
OtherClass otherClass();
}
@Component(modules={OtherModule.class})
@Singleton
public interface ApplicationComponent extends OtherComponent {
void inject(MainActivity mainActivity);
}
_
Attention: : vous devez fournir l'annotation _@Scope
_ (comme _@Singleton
_ ou _@ActivityScope
_) sur le module _@Provides
_ méthode annotée pour obtenir un fournisseur périmé au sein de votre composant généré, sinon, il ne sera pas périmé et vous obtiendrez une nouvelle instance chaque fois que vous injecterez.
3.2.) Créez un composant à portée d'application qui spécifie ce que vous pouvez injecter (identique à _injects={MainActivity.class}
_ dans Dagger 1.x ):
_@Singleton
@Component(module={AppContextModule.class}) //this is where you would add additional modules, and a dependency if you want to subscope
public interface ApplicationComponent extends AppContextComponent { //extend to have the provision methods
void inject(MainActivity mainActivity);
}
_
3.3.) Pour les dépendances que vous pouvez créez via un constructeur vous-même et ne voudrez pas redéfinir en utilisant un _@Module
_ (par exemple, vous utilisez des variantes de construction pour modifier le type d'implémentation), vous pouvez utiliser le constructeur annoté _@Inject
_.
_public class Something {
OtherThing otherThing;
@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}
_
De même, si vous utilisez le constructeur _@Inject
_, vous pouvez utiliser l'injection de champ sans avoir à appeler explicitement component.inject(this)
:
_public class Something {
@Inject
OtherThing otherThing;
@Inject
public Something() {
}
}
_
Ces classes de constructeur _@Inject
_ sont automatiquement ajoutées au composant de la même portée sans avoir à les spécifier explicitement dans un module.
Une classe de constructeurs _@Singleton
_ scoped _@Inject
_ apparaît dans les composants _@Singleton
_ scoped.
_@Singleton // scoping
public class Something {
OtherThing otherThing;
@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}
_
3.4.) Après avoir défini une implémentation spécifique pour une interface donnée, procédez comme suit:
_public interface Something {
void doSomething();
}
@Singleton
public class SomethingImpl {
@Inject
AnotherThing anotherThing;
@Inject
public SomethingImpl() {
}
}
_
Vous devrez "lier" l'implémentation spécifique à l'interface avec un _@Module
_.
_@Module
public class SomethingModule {
@Provides
Something something(SomethingImpl something) {
return something;
}
}
_
Un raccourci pour cela depuis Dagger 2.4 est le suivant:
_@Module
public abstract class SomethingModule {
@Binds
abstract Something something(SomethingImpl something);
}
_
4.) créez une classe Injector
pour gérer votre composant au niveau de l'application (elle remplace le monolithique ObjectGraph
)
(note: _Rebuild Project
_ pour créer la classe de générateur DaggerApplicationComponent
à l'aide de APT)
_public enum Injector {
INSTANCE;
ApplicationComponent applicationComponent;
private Injector(){
}
static void initialize(CustomApplication customApplication) {
ApplicationComponent applicationComponent = DaggerApplicationComponent.builder()
.appContextModule(new AppContextModule(customApplication))
.build();
INSTANCE.applicationComponent = applicationComponent;
}
public static ApplicationComponent get() {
return INSTANCE.applicationComponent;
}
}
_
5.) créez votre classe CustomApplication
_public class CustomApplication
extends Application {
@Override
public void onCreate() {
super.onCreate();
Injector.initialize(this);
}
}
_
6.) ajoutez CustomApplication
à votre _AndroidManifest.xml
_.
_<application
Android:name=".CustomApplication"
...
_
7.) Injectez vos classes dans MainActivity
_public class MainActivity
extends AppCompatActivity {
@Inject
CustomApplication customApplication;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Injector.get().inject(this);
//customApplication is injected from component
}
}
_
8.) Profitez-en!
+ 1.) Vous pouvez spécifier Scope
pour vos composants avec lesquels vous pouvez créer Activity- composants à niveau de portée . Les sous-portées vous permettent de fournir des dépendances dont vous n’avez besoin que pour un sous-domaine donné, plutôt que pour l’ensemble de l’application. Généralement, chaque activité obtient son propre module avec cette configuration. Veuillez noter qu'il existe un fournisseur ciblé par composant , ce qui signifie que pour conserver l'instance de cette activité, le composant lui-même doit survivre au changement de configuration. Par exemple, il pourrait survivre grâce à onRetainCustomNonConfigurationInstance()
ou à une étendue Mortar.
Pour plus d'informations sur le sous-classement, consultez le guide de Google . Voir aussi ce site à propos des méthodes de provision et aussi section des dépendances des composants ) et ici .
Pour créer une étendue personnalisée, vous devez spécifier l'annotation de qualificateur d'étendue:
_@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}
_
Pour créer une sous-étendue, vous devez spécifier l'étendue de votre composant et spécifier ApplicationComponent
comme dépendance. Bien entendu, vous devez également spécifier le sous-champ sur les méthodes du fournisseur de module.
_@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
extends ApplicationComponent {
CustomScopeClass customScopeClass();
void inject(YourScopedClass scopedClass);
}
_
Et
_@Module
public class CustomScopeModule {
@Provides
@YourCustomScope
public CustomScopeClass customScopeClass() {
return new CustomScopeClassImpl();
}
}
_
Veuillez noter que seul un composant couvert peut être spécifié en tant que dépendance. Pensez-y exactement à la manière dont l'héritage multiple n'est pas pris en charge en Java.
+ 2.) À propos de _@Subcomponent
_: essentiellement, une portée _@Subcomponent
_ peut remplacer une dépendance de composant; mais au lieu d'utiliser un générateur fourni par le processeur d'annotation, vous devez utiliser une méthode de fabrique de composants.
Donc ça:
_@Singleton
@Component
public interface ApplicationComponent {
}
@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
extends ApplicationComponent {
CustomScopeClass customScopeClass();
void inject(YourScopedClass scopedClass);
}
_
Devient ceci:
_@Singleton
@Component
public interface ApplicationComponent {
YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule);
}
@Subcomponent(modules={CustomScopeModule.class})
@YourCustomScope
public interface YourCustomScopedComponent {
CustomScopeClass customScopeClass();
}
_
Et ça:
_DaggerYourCustomScopedComponent.builder()
.applicationComponent(Injector.get())
.customScopeModule(new CustomScopeModule())
.build();
_
Devient ceci:
_Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());
_
+ 3.): Veuillez vérifier d'autres questions relatives au débordement de pile concernant Dagger2 également, elles fournissent de nombreuses informations. Par exemple, ma structure actuelle Dagger2 est spécifiée dans cette réponse .
Merci
Merci pour les guides sur Github , TutsPlus , Joe Steele , Froger MCS et Google .
Aussi pour cela guide de migration étape par étape, j'ai trouvé après avoir écrit ce post.
Et pour explication de la portée par Kirill.
Encore plus d'informations dans le documentation officielle .
Guide pour Dagger 1.x:
Les étapes sont les suivantes:
1.) ajoutez Dagger
au fichier build.gradle
Pour les dépendances
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
...
compile 'com.squareup.dagger:dagger:1.2.2'
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
Ajoutez également packaging-option
Pour éviter une erreur concernant duplicate APKs
.
Android {
...
packagingOptions {
// Exclude file to avoid
// Error: Duplicate files during packaging of APK
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
}
2.) créez une classe Injector
pour gérer le ObjectGraph
.
public enum Injector
{
INSTANCE;
private ObjectGraph objectGraph = null;
public void init(final Object rootModule)
{
if(objectGraph == null)
{
objectGraph = ObjectGraph.create(rootModule);
}
else
{
objectGraph = objectGraph.plus(rootModule);
}
// Inject statics
objectGraph.injectStatics();
}
public void init(final Object rootModule, final Object target)
{
init(rootModule);
inject(target);
}
public void inject(final Object target)
{
objectGraph.inject(target);
}
public <T> T resolve(Class<T> type)
{
return objectGraph.get(type);
}
}
3.) Créez un RootModule
pour relier vos futurs modules. Veuillez noter que vous devez inclure injects
pour spécifier chaque classe dans laquelle vous utiliserez l'annotation @Inject
, Car sinon Dagger jette RuntimeException
.
@Module(
includes = {
UtilsModule.class,
NetworkingModule.class
},
injects = {
MainActivity.class
}
)
public class RootModule
{
}
4.) Si vous avez d'autres sous-modules dans vos modules spécifiés dans votre racine, créez des modules pour ceux-ci:
@Module(
includes = {
SerializerModule.class,
CertUtilModule.class
}
)
public class UtilsModule
{
}
5.) Créez les modules feuille qui reçoivent les dépendances en tant que paramètres de constructeur. Dans mon cas, il n'y avait pas de dépendance circulaire, donc je ne sais pas si Dagger peut résoudre ce problème, mais je trouve cela peu probable. Les paramètres du constructeur doivent également être fournis dans un module par poignard. Si vous spécifiez complete = false
, Ils peuvent également figurer dans d'autres modules.
@Module(complete = false, library = true)
public class NetworkingModule
{
@Provides
public ClientAuthAuthenticator providesClientAuthAuthenticator()
{
return new ClientAuthAuthenticator();
}
@Provides
public ClientCertWebRequestor providesClientCertWebRequestor(ClientAuthAuthenticator clientAuthAuthenticator)
{
return new ClientCertWebRequestor(clientAuthAuthenticator);
}
@Provides
public ServerCommunicator providesServerCommunicator(ClientCertWebRequestor clientCertWebRequestor)
{
return new ServerCommunicator(clientCertWebRequestor);
}
}
6.) Étendre Application
et initialiser le Injector
.
@Override
public void onCreate()
{
super.onCreate();
Injector.INSTANCE.init(new RootModule());
}
7.) Dans votre MainActivity
, appelez l'injecteur à l'aide de la méthode onCreate()
.
@Override
protected void onCreate(Bundle savedInstanceState)
{
Injector.INSTANCE.inject(this);
super.onCreate(savedInstanceState);
...
8.) Utilisez @Inject
Dans votre MainActivity
.
public class MainActivity extends ActionBarActivity
{
@Inject
public ServerCommunicator serverCommunicator;
...
Si vous obtenez l'erreur no injectable constructor found
, Assurez-vous de ne pas oublier les annotations @Provides
.
Vous pouvez trouver ici des exemples de projets et de tutoriels utiles pour Dagger2.