Search code examples
androiddependency-injectiondagger-2android-architecture-components

Dagger2 dependency injection: How does it work in an android app?


I am trying to understand an example for an app with offline support using retrofit and room:

You can find the code for it here:

This project is using dependency injections with Dagger2. I've never worked with it so I am trying to understand how things work together. I understand the purpose of dependency injection but I don't understand the implementation of the project above.

I found a very good introduction into Dagger2 here:

A Friendly Introduction to Dagger 2

Dagger 2 example Code:

I worked through it and got most of it. Back to the actual project I am trying to understand (link 2). It still doesn't make sense to me and here is why:

  1. The interface AppComponent has one method which is used: public void inject(MainActivity2ViewModel viewModelModule); The return type is void. In the Dagger2 sample project (link 3 and 4) they use WeatherReporter getWeatherReporter(); which makes sense because later they call this method to get a WeatherReporter-Instance and Dagger2 manages all of the instantation process in the background. But I get nothing if the return type is void. Why is the return type not an object?

  2. There is one @Inject in MainActivity2ViewModel:

    @Inject public void setRepository(GitHubRepository2 repository) { this.repository = repository; }

repository is the only field of MainActivity2ViewModel so it is a dependency. The GitHubRepository2 constructor has 3 parameters:

    @Inject
    public GitHubRepository2(GitHubApi api, GitHubDao dao, Executor executor)

For each of them there is a module explaining how to create those objects. But why is there an AppModule and a NetModule? Maybe the AppModule is there because DaoModule needs an Application reference but why is there a NetModule and where is it used?


Solution

  • There's a lot of comprehensive tutorials about Dagger2 in Android. But I'll show you a glimpse of what it's used for. And minimal usage.

    Ultimately, dagger will use the annotation @Inject which will provide(reference to the object or value) to the variable.

    Injection is usually used on reusable or boilerplate objects like Dao, Repository, ViewModel, NetworkAdapter

    class SomethingThatRequiresNetwork { // Activity, Fragment
        @Inject
        MyReusableNetworkAdapter myReusableNetworkAdapter;
    
        String baseUrl; // for example purpose only
        SomeDependency someDependency;
    
        void init() {
            // @NOTE: DaggerMyExampleComponent is a generated class. It will be red before compilation.
            MyExampleComponent MyExampleComponent = DaggerMyExampleComponent.builder().build();
            MyExampleComponent.inject(this); // the actual injection happens here
        }
    
        // yes, you can just use @Inject on the variables directly but this is another use.
        @Inject
        void methodInjection(String baseUrl, SomeDependency someDependency) {
            this.baseUrl = baseUrl;
            this.someDependency = someDependency;
        }
    }
    
    // ANSWER to the two questions
    // this is a pseudocode of the generated code. You do not write this
    // MyExampleComponent class
    void inject(SomethingThatRequiresNetwork obj) {
        // @NOTE: modules are actually instantiated by MyExampleComponent. Not called statically. I just shortened it
        obj.myReusableNetworkAdapter = NetModule.provideNetworkAdapter();
        obj.methodInjection(NetModule.provideBaseUrl(), SomeModule.provideSomeDependency());
    }
    
    // these here are modules that provide by return TYPE
    // you write these
    
    @Module
    class NetModule {
        @Provides
        @Singleton
        String provideBaseUrl() {
            return "www.some-url.com";
        }
    
        @Provides
        @Singleton // will store the object and reuse it.
        // @NOTE: provision can work internally within modules or inter-module. the input here is provided by provideBaseUrl
        MyReusableNetworkAdapter provideNetworkAdapter(String baseUrl) {
            return new MyReusableNetworkAdapter(baseUrl);
        }
    
    }
    
    @Modules
    class SomeModule {
        @Provides
        @Singleton
        SomeDependency provideSomeDependency() {
            return new SomeDependency();
        }
    }
    
    // Component. uses modules
    
    @Singleton // .build() will reuse
    @Component(modules = {NetModule.class, SomeModule.class})
    interface MyExampleComponent {
        // the method name doesn't matter
        // the class type does matter though.
        void inject(SomethingThatRequiresNetwork somethingThatRequiresNetwork);
    
        // some other class that needs injection. @NOTE: I did not give example for this
        void inject(SomethingThatRequiresDependency some); 
    
    }
    
    

    NOTE. This code is usually written from bottom to top lol. You start writing the Component then Module then Injections.

    Just follow the calls from the top of this answer and you'll figure out how Dagger2 works.