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
Open Soft Keyboard Programmatically
How to Get Name of Wifi-Network Out of Android Using Android API
Cancel Notification on Remove Application from Multitask Panel
Set Selected Item in Android Bottomnavigationview
Notificationcompat with API 26
Error Message:This Android Sdk Requires Android Developer Toolkit Version 22.6.1 or Above
Run Code When Android App Is Closed/Sent to Background
Flutter Projects & Android X Migration Issues
Android: Retrieve Contact Name from Phone Number
Simulate Low Battery & Low Memory in Android
Locationlistener of Network_Provider Is Enabled But , Onlocationchanged Is Never Called
Difference Between Surfaceview and View