Dagger2: Component cannot depend on multiple scoped components - android

Yes, I know this has been asked before, and yes, I know it is "by design".
But I'd like to do something like this:
#Component(modules = {RealmModule.class})
public interface RealmComponent {
Realm realm();
}
#Component(modules = {RepositoryModule.class})
public interface RepositoryComponent {
PersonRepository personRepository();
ScheduleRepository schedulesRepository();
}
#Component(dependencies = {RealmComponent.class, RepositoryComponent.class})
public interface AppDataComponent
extends RealmComponent, RepositoryComponent {
}
#ApplicationScope
#Component(dependencies = {AppContextComponent.class,
AppDataComponent.class,
AppDomainComponent.class,
AppPresentationComponent.class,
AppUtilsComponent.class})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
void inject(CustomApplication customApplication);
void inject(DashboardActivity dashboardActivity);
}
However, what I get is unscoped, every time I inject a JobManager or a ScheduleRepository or anything else, I get a new instance. The only way I could "fix" that was this.
#Module
public class JobManagerModule {
private JobManager jobManager;
#Provides
public JobManager jobManager(Context context) {
if(jobManager == null) {
jobManager = new JobManager(context, new Configuration.Builder(context).networkUtil(
new WifiOrMobileNetworkUtil(context)).build());
}
return jobManager;
}
}
Not a fan.
So, how is one meant to structure and rip apart the dependency tree, without making one big gigantic über blob component that has every single module listed and every single provision method (instead of these "subcomponent" component dependencies)?
I tried using subcomponents for this, but then you have to provide every single module for the final ApplicationComponent.
I'm not sure what to do here. I tried specifying #Singleton for every first-level component and #SubcomponentScope for every AppDataLevelComponent, I also tried making a new scope for every single subcomponent, but both of them failed with "cannot depend on multiple scoped components".
EDIT: Apparently in order to get scoped providers, marking the components with the scope is not enough - you must specify the scope for the #Provides annotated methods too.
#Module
public class RepositoryModule {
#Provides
#Singleton
public PersonRepository personRepository() {
return new PersonRepositoryImpl();
}
#Provides
#Singleton
public ScheduleRepository schedulesRepository() {
return new SchedulesRepositoryImpl();
}
}
In the meantime, I ended up with this übercomponent.
#Singleton
#Component(modules = {
AppContextModule.class,
DbMapperModule.class,
DbTaskModule.class,
RealmModule.class,
RepositoryModule.class,
InteractorModule.class,
ServiceModule.class,
PresenterModule.class,
XmlPersisterModule.class
})
public interface ApplicationComponent
extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
Where the xyzComponent classes are just interfaces to store the provision methods...
(Please note that this structure is an anti-pattern as described by Martin Fowler, and you should organize modules based on features / activities, and make them into subscoped components using component dependencies. Component dependencies are used to subscope your superscope components, and "inherit" dependency providers.)

I had same problems like you not while ago and ended using the same ubercomponent approach except I use #Subcomponents in order organize the things and not to have all modules listed in the ubercomponent (I call it "top" or "app's" component).
You may see an example here:
How to migrate missing inject from module with complete = false from Dagger 1 to Dagger 2

Related

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:
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?
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?
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.

Difference between the #Singleton in module and #Singleton in component in Dagger 2

Im learning Dagger 2, i have noticed that in some examples there is a #Singleton in the module's methods and on other #Singleton on the Component's methods? What is the difference and what exactly a #Singleton annotation on a module's method and on an component's method mean?
Since you're a beginner, I highly recommend just trying stuff. Writing unit tests is easy enough, and it helps to understand and prove theories.
If you didn't already, read the User's Guide for some basic knowledge about dagger and scopes.
Annotating methods in components (provision methods) doesn't have any effect. You will have to annotate the class or providing method in a module. I want to quickly show how you can just quickly prove this yourself:
We have 2 Component, one using a scope #Singleton, the other one none:
#Singleton
#Component(modules = SingletonModule.class)
public interface SingletonComponent {
Object getObject();
}
#Component(modules = NormalModule.class)
public interface NormalComponent {
#Singleton
Object getObject();
}
With those components come 2 modules, one providing the singleton scoped object (same as the component) the other one just uses no scope:
#Module
public class SingletonModule {
#Provides
#Singleton
public Object provideObject() {
return new Object();
}
}
#Module
public class NormalModule {
#Provides
public Object provideObject() {
return new Object();
}
}
And now we just create a small test:
public class ComponentTest {
#Test
public void testSingletonComponent() {
SingletonComponent component = DaggerSingletonComponent.create();
Assert.assertEquals(component.getObject(), component.getObject());
}
#Test
public void testNormalComponent() {
NormalComponent component = DaggerNormalComponent.create();
Assert.assertNotSame(component.getObject(), component.getObject());
}
}
This test will succeed and prove that annotating methods in components doesn't do anything. Scoping objects in modules, or annotating the class itself when using constructor injection will result in the object being reused within the same scope / the same component.
Creating 2 components of the same scope will also lead to duplicate objects, as can be proven like this:
#Test
public void testTwoSingleonComponents() {
SingletonComponent component1 = DaggerSingletonComponent.create();
SingletonComponent component2 = DaggerSingletonComponent.create();
Assert.assertNotSame(component1.getObject(), component2.getObject());
}
Be sure to read some tutorials and be sure to try things out. The compiler will complain if you do things wrong! :)

Dagger2: Error when two components has same inject method signature

I have this component:
#Singleton
#Component(modules = OauthModule.class)
public interface OauthComponent {
void inject(LoginActivity a);
}
and the module:
#Module
public class OauthModule {
#Provides
#Singleton
Oauth2Service provideOauth2Service() {
return new Oauth2StaticService();
}
}
and this another component:
#Singleton
#Component(modules = LoggedUserModule.class)
public interface LoggedUserComponent {
void inject(LoginActivity a);
}
and I get this error:
Error:(15, 10) error: Oauth2Service cannot be provided without an
#Provides- or #Produces-annotated method.
If I change the LoggedUserComponent's inject method parameter to be another Activity, say AnotherActivity like this:
#Singleton
#Component(modules = LoggedUserModule.class)
public interface LoggedUserComponent {
void inject(AnotherActivity a);
}
compilation is ok. Why? Can't I have two components with the same inject signature?
I'm trying to understand how Dagger works so any help will be appreciated. Thanks.
Think of dagger as an object graph—which it actually is. You probably should not have 2 different components being able to inject the same object, other than for testing purposes (or if you want to include different behavior, not additional one).
If your LoginActivity depends on multiple modules, you should aggregate them in a single component, since as your error shows, dagger will fail if it can not provide all dependencies from a single component.
#Singleton
#Component(modules = {LoggedUserModule.class, OauthModule.class})
public interface LoggedUserComponent {
void inject(AnotherActivity a);
}
Looking at Oauth2Service, this could easily be something that multiple objects could use, so a higher scope could be adequate. In that case you should think about adding it with a #Singleton scope to your application component, or maybe create its own component with e.g. a #UserScope.
Then you would have to either make your LoggedUserComponent a #Subcomponent or declare this component a dependency using #Component(dependencies = OauthComponent.class) and providing a getter in OauthComponent for it. In both cases dagger would also be able to provide the dependency found higher in the graph, thus also resolving your error.
It's getting mad because you're saying you can inject into that class but you're not providing a class that it's expecting you to provide. You just need to add the OauthModule to your LoggedUserComponent. Try this
#Singleton
#Component(modules = {LoggedUserModule.class, OauthModule.class})
public interface LoggedUserComponent {
void inject(LoginActivity loginActivity);
}

Dagger2 what is the purpose of adding a scope tag to components?

I've created a component and it only last for the lifetime of the activity. I did not use any scope annotations and only quick example of the life time of the component looks like this:
public class MainActivity extends AppCompatActivity {
private final String TAG = getClass().getSimpleName();
#Inject
AlmondButter someAlmondButter;
#Inject
CashewSandwich sandwich;
SandwichComponent sandwichComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*create thte dependent butter for the sandwich here*/
ButterComponent butterComponent=DaggerButterComponent.builder().
butterModule(new ButterModule()).build();
/*create a scope sandwichcomponent here */
sandwichComponent=DaggerSandwichComponent.builder().sandwichModule(new SandwichModule()).
butterComponent(butterComponent)
.build();
//finally we have a sandwichComponent, lets inject our dependencies
sandwichComponent.inject(this);
Log.v(TAG,sandwich.toString());
Log.v(TAG,someAlmondButter.toString());
}
#Override
protected void onDestroy() {
super.onDestroy();
//not necessary but it clearly shows the scope being tied to lifecycle of activity
sandwichComponent=null;
}
}
none of my components are scoped with anotations and it works fine. So im confused why some recommend to create scope tags, what is there purpose ? i'll show you my components below for reference:
#Component(dependencies = ButterComponent.class, modules = SandwichModule.class)
public interface SandwichComponent {
CashewSandwich ProvideCashewSandwitch();
void inject (MainActivity mainactivity);
}
and the next component:
#Component(modules={ButterModule.class})
public interface ButterComponent {
//these are for our whatever class depends on butter
AlmondButter ProvideAlmondButter();
CashewButter ProvideCashewButter();
}
UPDATE: FOR ANYONE WHO NEEDS HELP WITH UNDERSTANDING THESE CONCEPTS I MADE A BLOG HERE.
By using scopes on component and scopes on module provider method, you can ask Dagger2 to create scoped providers for you.
In order to get a scoped provider in your module's provider method, you must put the scope on the component as well.
You can specify only one scope on a given component, and in a scoped component, you can only have modules with that scope on its provider methods, or the provider methods can also be unscoped.
Unscoped providers give you a new instance on every inject call.
Scoped providers store a single instance for every inject call to that specific component instance.
#Component(modules={HelloModule.class})
#Singleton
public interface HelloComponent {
Hello hello();
World world();
void inject(MainActivity mainActivity);
}
#Module
public class HelloModule {
#Provides
public Hello hello() { return new Hello(); } //new instance each call to inject
#Provides
#Singleton
public World world() { return new World(); } //one instance per component
}
It is also worth noting that if you subscope another component to inherit its dependencies (using subcomponent or component dependency), you can only depend on one other scoped component. Think of it like how "multiple inheritance" is not allowed in Java, you also cannot depend on multiple scoped components and inherit their dependencies, only one.
Typically you have a singleton scope, and you subscope your components according to the top-level separation of modules in your application.
#Component(modules={ApplicationModule.class})
#Singleton
public interface ApplicationComponent {
Something something();
}
#Component(dependencies={ApplicationComponent.class}, modules={MainActivityModule.class})
#ActivityScope
//this is a subscoped component that inherits from ApplicationComponent
public interface MainActivityComponent extends ApplicationComponent {
OtherThing otherThing();
void inject(MainActivity mainActivity);
}
According to Martin Fowler, the right way to slice your application into pieces on the top-level is by features, such as GalleryComponent, SettingsComponent, etc. and not by layers (data, domain, presentation).
Scopes manage the instance creation across multiple requests for the same type. Imagine if you had this:
#Inject
AlmondButter someAlmondButter;
#Inject
AlmondButter otherAlmondButter;
This would create two separate AlmondButter instances. A trivial case, but hopefully it illustrates the point that each time you request the dependency, a new one is created.
Imagine now you have two different classes, each with a field #Inject AlmondButter sharedAlmondButter. If you want them to have the same exact instance, a scope will handle that for you.
Similarly, with any dependency you have, you can inject a Provider<T>, i.e. #Inject Provider<AlmondButter> almondButterProvider. This can allow you to call almondButterProvider.get() to retrieve a new instance. If you then wanted all values returned by .get() to be the same instance, a scope would accomplish the same thing.

Dagger2: Make parent component dependency accessible for subscoped component

I have the following setup:
#ApplicationScope
#Component(
dependencies = {AppContextComponent.class, CertUtilsComponent.class, ServiceComponent.class, JobManagerComponent.class})
public interface ApplicationComponent
extends AppContextComponent, CertUtilsComponent, ServiceComponent, JobManagerComponent {
void inject(MainActivity mainActivity); //honestly, I won't need this
}
And I have the following subscoped component:
#PresenterScope
#Component(dependencies = {ApplicationComponent.class, PersistenceComponent.class})
public interface PresenterComponent
extends ApplicationComponent, PersistenceComponent {
void inject(HomePresenter homePresenter);
void inject(SendCertificateRequestInteractor sendCertificateRequestInteractor);
}
The problem is that PersistenceComponent has the following component as its dependency:
#Component(dependencies = {JobManagerComponent.class, RealmComponent.class, RepositoryComponent.class}, modules = {PersisterModule.class})
public interface PersisterComponent {
DummyCertPersister dummyCertPersister();
}
Which uses JobManagerComponent, which is the dependency of the ApplicationComponent.
Unfortunately, this component does not seem to be inherited from ApplicationComponent. I need to explicitly keep track of this component, and provide it for the builder like so.
//INJECTOR
ApplicationComponent applicationComponent = DaggerApplicationComponent.builder()
.appContextComponent(appContextComponent)
.certUtilsComponent(certUtilsComponent)
.jobManagerComponent(jobManagerComponent)
.serviceComponent(serviceComponent)
.build();
this.jobManagerComponent = jobManagerComponent;
this.applicationComponent = applicationComponent;
this.certUtilsComponent = certUtilsComponent;
this.appContextComponent = appContextComponent;
this.serviceComponent = serviceComponent;
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
public JobManagerComponent getJobManagerComponent() {
return jobManagerComponent;
}
And provide this through a getter when I build my persistence component:
PersisterComponent persisterComponent = DaggerPersisterComponent.builder()
.jobManagerComponent(Injector.INSTANCE.getJobManagerComponent()) //this should be subcomponent
.realmComponent(realmComponent)
.repositoryComponent(repositoryComponent)
.persisterModule(new PersisterModule())
.build();
I would like to have that JobManagerComponent inherited from ApplicationComponent. I'm guessing I need to make this into a #Subcomponent and provide a provision method for it, but so far, it hasn't worked (I don't get a builder method, but it still doesn't see the jobManager within JobManagerComponent.
Is this possible with #Subcomponent? Or do I need to keep track of the "subcomponents" of ApplicationComponent in case a child component depends on it?
The answer is that I was conceptually wrong about how Components work in Dagger2.
A component should only depend on another component if it subscopes that component. A component with multiple component dependencies cannot depend on scoped components; which means they cannot have scoped modules; which also means they cannot use scopes at all and unless the module keeps a reference to the new instance, you'll get a new instance each time you inject. Which is bad, and not what you want at all.
The proper way is to bind every module of the same scope to the same component. That way, the provided dependencies are accessible within the constructors, and you won't have an issue with sharing the same module within different components as well.
Therefore, the approach in the question is completely wrong.
Use this instead.

Categories

Resources