Also, add packaging-option to prevent an error about duplicate APKs.
android {
...
packagingOptions {
// Exclude file to avoid
// Error: Duplicate files during packaging of APK
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
}
2.) create an Injector class to handle the 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.) Create a RootModule to link your future modules together. Please note that you must include injects to specify every class in which you will use @Inject annotation, because otherwise Dagger throws RuntimeException.
@Module(
includes = {
UtilsModule.class,
NetworkingModule.class
},
injects = {
MainActivity.class
}
)
public class RootModule
{
}
4.) In case you have other sub-modules within your modules specified in your Root, create modules for those:
@Module(
includes = {
SerializerModule.class,
CertUtilModule.class
}
)
public class UtilsModule
{
}
5.) create the leaf modules which receive the dependencies as constructor parameters. In my case, there was no circular dependency, so I don't know if Dagger can resolve that, but I find it unlikely. The constructor parameters must also be provided in a Module by Dagger, if you specify complete = false then it can be in other Modules too.
@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.) Extend Application and initialize the Injector.
@Override
public void onCreate()
{
super.onCreate();
Injector.INSTANCE.init(new RootModule());
}
7.) In your MainActivity, call the Injector in the onCreate() method.
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.
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 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.
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.
@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.
+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.