How to Set Up Dagger Dependency Injection from Scratch in Android Project

How to set up DAGGER dependency injection from scratch in Android project?

Guide for Dagger 2.x (Revised Edition 6):

The steps are the following:

1.) add Dagger to your build.gradle files:

  • top level 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()
}
}
  • app level 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.) Create your AppContextModule class that provides the dependencies.

@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.) create the AppContextComponent class that provides the interface to get the classes that are injectable.

public interface AppContextComponent {
CustomApplication application(); //provision method
Context applicationContext(); //provision method
LocationManager locationManager(); //provision method
}

3.1.) This is how you would create a module with an implementation:

@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);
}

Beware:: You need to provide the @Scope annotation (like @Singleton or @ActivityScope) on the module's @Provides annotated method to get a scoped provider within your generated component, otherwise it will be unscoped, and you'll get a new instance each time you inject.

3.2.) Create an Application-scoped component that specifies what you can inject (this is the same as the injects={MainActivity.class} in 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.) For dependencies that you can create via a constructor yourself and won't want to redefine using a @Module (for example, you use build flavors instead to change the type of implementation), you can use @Inject annotated constructor.

public class Something {
OtherThing otherThing;

@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}

Also, if you use @Inject constructor, you can use field injection without having to explicitly call component.inject(this):

public class Something {
@Inject
OtherThing otherThing;

@Inject
public Something() {
}
}

These @Inject constructor classes are automatically added to the component of the same scope without having to explicitly specify them in a module.

A @Singleton scoped @Inject constructor class will be seen in @Singleton scoped components.

@Singleton // scoping
public class Something {
OtherThing otherThing;

@Inject
public Something(OtherThing otherThing) {
this.otherThing = otherThing;
}
}

3.4.) After you've defined a specific implementation for a given interface, like so:

public interface Something {
void doSomething();
}

@Singleton
public class SomethingImpl {
@Inject
AnotherThing anotherThing;

@Inject
public SomethingImpl() {
}
}

You'll need to "bind" the specific implementation to the interface with a @Module.

@Module
public class SomethingModule {
@Provides
Something something(SomethingImpl something) {
return something;
}
}

A short-hand for this since Dagger 2.4 is the following:

@Module
public abstract class SomethingModule {
@Binds
abstract Something something(SomethingImpl something);
}

4.) create an Injector class to handle your application-level component (it replaces the monolithic ObjectGraph)

(note: Rebuild Project to create the DaggerApplicationComponent builder class using 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.) create your CustomApplication class

public class CustomApplication
extends Application {
@Override
public void onCreate() {
super.onCreate();
Injector.initialize(this);
}
}

6.) add CustomApplication to your AndroidManifest.xml.

<application
android:name=".CustomApplication"
...

7.) Inject your classes in 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.) Enjoy!

+1.) You can specify Scope for your components with which you can create Activity-level scoped components. Subscopes allow you to provide dependencies that you only need only for a given subscope, rather than throughout the whole application. Typically, each Activity gets its own module with this setup. Please note that a scoped provider exists per component, meaning in order to retain the instance for that activity, the component itself must survive configuration change. For example, it could survive through onRetainCustomNonConfigurationInstance(), or a Mortar scope.

For more info on subscoping, check out the guide by Google. Also please see this site about provision methods and also the component dependencies section) and here.

To create a custom scope, you must specify the scope qualifier annotation:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}

To create a subscope, you need to specify the scope on your component, and specify ApplicationComponent as its dependency. Obviously you need to specify the subscope on the module provider methods too.

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
extends ApplicationComponent {
CustomScopeClass customScopeClass();

void inject(YourScopedClass scopedClass);
}

And

@Module
public class CustomScopeModule {
@Provides
@YourCustomScope
public CustomScopeClass customScopeClass() {
return new CustomScopeClassImpl();
}
}

Please note that only one scoped component can be specified as a dependency. Think of it exactly like how multiple inheritance is not supported in Java.

+2.) About @Subcomponent: essentially, a scoped @Subcomponent can replace a component dependency; but rather than using a builder provided by the annotation processor, you would need to use a component factory method.

So this:

@Singleton
@Component
public interface ApplicationComponent {
}

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
extends ApplicationComponent {
CustomScopeClass customScopeClass();

void inject(YourScopedClass scopedClass);
}

Becomes this:

@Singleton
@Component
public interface ApplicationComponent {
YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule);
}

@Subcomponent(modules={CustomScopeModule.class})
@YourCustomScope
public interface YourCustomScopedComponent {
CustomScopeClass customScopeClass();
}

And this:

DaggerYourCustomScopedComponent.builder()
.applicationComponent(Injector.get())
.customScopeModule(new CustomScopeModule())
.build();

Becomes this:

Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());

+3.): Please check other Stack Overflow questions regarding Dagger2 as well, they provide a lot of info. For example, my current Dagger2 structure is specified in this answer.

Thanks

Thank you for the guides at Github, TutsPlus, Joe Steele, Froger MCS and Google.

Also for this step by step migration guide I found after writing this post.

And for scope explanation by Kirill.

Even more information in the official documentation.

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

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.

How to inject repository dagger dependency instance in my integration tests?

You have to pass reference of AppIntegTests class to dagger graph in order to initialize lateinit property by dagger

Add inject function to TestAppComponent

interface TestAppComponent  {

fun inject(test: AppIntegTests)

@Component.Builder
interface Builder {
fun build(): TestAppComponent
}
}

call this method from setUp function of AppIntegTests

class AppIntegTests {

@Inject
lateinit var repository: IRepository

@Before
fun setup() {
val testAppComponent = DaggerTestAppComponent.builder().build()
testAppComponent.inject(this)

repository.deleteAll()
}

Dagger2 and Android

Components ought to be the "big DI provider" that provides everything for a specific scope.

For example, you could have a SingletonComponent with @Singleton scope that has every single module added to it that has at least one @Singleton scoped provider method.

@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent {
// provision methods
OkHttpClient okHttpClient();
RealmHolder realmHolder();
// etc.
}

You can have the provision methods defined per module.

public interface DatabaseComponent {
RealmHolder realmHolder();
}

public interface NetworkingComponent{
OkHttpClient okHttpClient();
}

In which case you'd have

@Singleton
@Component(modules={NetworkingModule.class, DatabaseModule.class, MapperModule.class, UtilsModule.class})
public interface SingletonComponent
extends NetworkingComponent, DatabaseComponent, MapperComponent, UtilsComponent {
// provision methods inherited
}


In a Module, you can specify a factory method ("provider method") that specifies how to create a particular type of dependency.

For example,

@Module
public class NetworkingModule {
@Provides
@Singleton
OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()./*...*/.build();
}

@Provides
@Singleton
Retrofit retrofit(OkHttpClient okHttpClient) {
// ...
}
}

You can imagine the @Singleton scope as the big DI container that Spring would have given you.


You can also provide instances of the class using @Inject annotated constructor. This can receive any class from the component that is able to instantiate it from provider methods within that scoped component's modules (and unscoped dependencies, of course).

@Singleton
public class MyMapper {
@Inject
public MyMapper(RealmHolder realmHolder, OkHttpClient okHttpClient) { // totally random constructor for demo
}
}

or

@Singleton
public class MyMapper {
@Inject
RealmHolder realmHolder;

@Inject
OkHttpClient okHttpClient;

@Inject
public MyMapper() {
}
}

Then this will be available in the component, you can even make provision method for it to make it inheritable in component dependencies:

@Singleton 
@Component(modules={...})
public interface SingletonComponent {
MyMapper myMapper();
}


With Dagger2, you can also additionally create "subscoped components", that inherit all dependencies provided from a component of a given scope.

For example, you can inherit all @Singleton scoped components, but you can still have new scoped dependencies per that new scope, such as @ActivityScope.

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

Then, you can create subscoped components using either subcomponents, or component dependencies.


  • Subcomponent:

.

@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
MainPresenter mainPresenter();
}

Then this can be created in its parent scoped component:

@Singleton 
@Component(modules={...})
public interface SingletonComponent {
MainActivityComponent mainActivityComponent(MainActivityModule module);
}

Then you can use the singleton component to instantiate this:

SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = singletonComponent.mainActivityComponent(new MainActivityModule(mainActivityHolder));

  • Component dependency:

.

@ActivityScope
@Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
public interface MainActivityComponent extends SingletonComponent {
MainPresenter mainPresenter();
}

For this to work, you must specify provision methods in the superscoped component.

Then you can instantiate this like so:

SingletonComponent singletonComponent = DaggerSingletonComponent.create();
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(singletonComponent)
.mainActivityModule(new MainActivityModule(mainActivityHolder))
.build();


In Dagger2, you can therefore obtain dependencies either via:

  • @Inject annotated constructor parameters
  • @Inject annotated fields on classes with @Inject annotated constructor
  • from @Component provision methods
  • via manual field injection method defined in component (for classes you can't create using @Inject annotated constructor)

Manual field injection can happen for classes like MainActivity, that you yourself don't create.

Manual field injection injects only the specific class that you are injecting. Base-classes don't get automatically injected, they need to call .inject(this) on component.

It works like this:

@ActivityScope
@Subcomponent(modules={MainActivityModule.class})
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}

Then you can do:

public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(getSingletonComponent())
.mainActivityModule(new MainActivityModule(this))
.build(); // ensure activity `holder` instead, and retain component in retained fragment or `non-configuration instance`
mainActivityComponent.inject(this);
}
}

Dependency Injection in multiple Activities with Dagger and Room

to give you a bit of a more complete answer, taken from this online resource https://github.com/MindorksOpenSource/android-dagger2-example/blob/master/app/src/main/java/com/mindorks/example/android_dagger2_example/DemoApplication.java

(i'm just going over it briefly to give you a better understanding)

Create an application class which extends Application and implement this :

     @Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
}

you have to define your own applicationComponent though, just going through this briefly.
after that, you should be able to use your repositories in several different places with the annotation of @Inject where you need them, just remember to actually call the injection where you need it, making use of

getActivityComponent().inject(this); or AndroidInjection.inject(this) where you need to, hopefully this github example and the tutorials are enough to answer some of your questions



Related Topics



Leave a reply



Submit