Guide pour Dagger 2.x (édition révisée 6) :
Les étapes sont les suivantes:
1.) ajoutez Dagger
à vos build.gradle
fichiers:
- build.gradle de niveau supérieur :
.
// 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()
}
}
- build.gradle au niveau de l' application :
.
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 AppContextModule
classe qui fournit 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éez la AppContextComponent
classe 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' @Scope
annotation (comme @Singleton
ou @ActivityScope
) sur la @Provides
méthode annotée du module pour obtenir un fournisseur de portée dans votre composant généré, sinon il sera sans portée, et vous obtiendrez une nouvelle instance à chaque fois que vous injecterez.
3.2.) Créez un composant d'application qui spécifie ce que vous pouvez injecter (c'est le même que celui injects={MainActivity.class}
de 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éer vous-même via un constructeur et que vous ne voudrez pas redéfinir en utilisant a @Module
(par exemple, vous utilisez plutôt des versions de build pour changer le type d'implémentation), vous pouvez utiliser un @Inject
constructeur annoté.
public class Something {
OtherThing otherThing;
@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}
De plus, si vous utilisez un @Inject
constructeur, vous pouvez utiliser l'injection de champ sans avoir à appeler explicitement component.inject(this)
:
public class Something {
@Inject
OtherThing otherThing;
@Inject
public Something() {
}
}
Ces @Inject
classes de constructeur sont automatiquement ajoutées au composant de même portée sans avoir à les spécifier explicitement dans un module.
Une classe de constructeur à @Singleton
portée @Inject
sera vue dans les @Singleton
composants à portée.
@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, comme ceci:
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 fichier @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 Injector
classe pour gérer votre composant au niveau de l'application (il remplace le monolithique ObjectGraph
)
(note: Rebuild Project
pour créer la DaggerApplicationComponent
classe de générateur en utilisant 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 CustomApplication
classe
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 dansMainActivity
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 des composants étendus au niveau de l'activité . Les sous-étendues vous permettent de fournir des dépendances dont vous n'avez besoin que pour une sous-étendue donnée, plutôt que dans toute l'application. En règle générale, chaque activité possède son propre module avec cette configuration. Veuillez noter qu'un fournisseur de portée existe par composant , ce qui signifie que pour conserver l'instance pour cette activité, le composant lui-même doit survivre au changement de configuration. Par exemple, il pourrait survivre à travers onRetainCustomNonConfigurationInstance()
, ou une lunette de mortier.
Pour plus d'informations sur le sous-champ, consultez le guide de Google . Veuillez également consulter ce site sur les méthodes de mise à disposition ainsi que la section sur les dépendances des composants ) et ici .
Pour créer une étendue personnalisée, vous devez spécifier l'annotation du qualificatif d'étendue:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}
Pour créer une sous-étendue, vous devez spécifier la portée de votre composant et la spécifier ApplicationComponent
comme sa dépendance. De toute évidence, vous devez également spécifier la sous-étendue 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();
}
}
S'il vous plaît noter que seule une composante scope peut être spécifié comme une dépendance. Pensez-y exactement comme l'héritage multiple n'est pas pris en charge en Java.
+2.) A propos @Subcomponent
: essentiellement, une portée @Subcomponent
peut remplacer une dépendance de composant; mais plutôt que d'utiliser un générateur fourni par le processeur d'annotations, vous devrez 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 également les autres questions de Stack Overflow concernant Dagger2, elles fournissent beaucoup d'informations. Par exemple, ma structure Dagger2 actuelle est spécifiée dans cette réponse .
Merci
Merci pour les guides de Github , TutsPlus , Joe Steele , Froger MCS et Google .
Aussi pour ce guide de migration étape par étape, j'ai trouvé après avoir écrit ce post.
Et pour l' explication de la portée par Kirill.
Encore plus d'informations dans la documentation officielle .