web-dev-qa-db-fra.com

Comment configurer l'injection de dépendance DAGGER à partir de zéro dans Android projet?

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".]

96
EpicPandaForce

Guide pour Dagger 2.x (Édition révisée 6):

Les étapes sont les suivantes:

1.) ajoutez Dagger à vos fichiers _build.gradle_:

  • niveau supérieur 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()
    }
}
_
  • niveau de l'application build.gradle :

.

_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 .

181
EpicPandaForce

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.

11
EpicPandaForce

Vous pouvez trouver ici des exemples de projets et de tutoriels utiles pour Dagger2.

projet d'exemple de poignard 2 avec MVP

tutoriel vidéo

tutoriel pratique

0
ramkrishna kushwaha