MVP with Dagger 2 and common object - android

I'm using MVP pattern with Dagger 2.
My project has two features using a common repository. It means I have to inject two times this repository, one time for each feature. But when It tried to do this I get this error : "...Repository is bound multiple times"
I found that this can be resolved using #Named. So I added this in my module but now I'm getting a new error "...Repository cannot be provided without an #Provides-annotated method."
I think I have to add this #Named elsewhere in my project to make it work properly since I got some link explaining this (like this one multiple instance of same object with named). The problem I'm quite new to all this and can't find where to add this #Names elsewhere in my project architecture.
So, I'm actually getting this error "...Repository cannot be provided without an #Provides-annotated method."
My project is structured like following.
==== A root package containing this three classes :
App class
public class App extends Application {
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
final String AUTH_TOKEN = getResources().getString(R.string.aqicn_token);
final String BASE_URL = getResources().getString(R.string.aqicn_api_base_url);
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.pollutionApiModule(new PollutionApiModule(BASE_URL))
.pollutionLevelsModule(new PollutionLevelsModule())
.build();
}
public ApplicationComponent getComponent() {
return component;
}
}
ApplicationComponent class
#Singleton
#Component(modules = {ApplicationModule.class, PollutionApiModule.class, PollutionLevelsModule.class, DonutModule.class})
public interface ApplicationComponent {
void injectPollutionLevels(PollutionLevelsFragment target);
void injectDonut(DonutFragment target);
}
ApplicationModule class
#Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
public Context provideContext() {
return application;
}
}
==== A pollutionlevels package containing a Dagger module, this package is MVP structured (Fragment, Model, Module, Presenter...) and is related to a single feature that get data from my common repository. The purpose of this feature is to show my data as text :
PollutionLevelModule class, you can see here I tried to add the #Name annotation trying to resolve my problem :
#Module
public class PollutionLevelsModule {
#Provides
public PollutionLevelsFragmentMVP.Presenter providePollutionLevelsFragmentPresenter(PollutionLevelsFragmentMVP.Model pollutionLevelsModel) {
return new PollutionLevelsPresenter(pollutionLevelsModel);
}
#Provides
public PollutionLevelsFragmentMVP.Model providePollutionLevelsFragmentModel(Repository repository) {
return new PollutionLevelsModel(repository);
}
#Singleton
#Provides
#Named("levelsRepo")
public Repository provideRepo(PollutionApiService pollutionApiService) {
return new CommonRepository(pollutionApiService);
}
}
This package contains a Fragment where I inject the repository in onActivityCreated(). Here I call the method injectPollutionLevels() implemented in my App class (the class I shew you just above) :
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((App) getActivity().getApplication()).getComponent().injectPollutionLevels(this);
}
==== A donut package containing a Dagger module, this package is MVP structured (Fragment, Model, Module, Presenter...) and is related to a single feature that get data from my common repository. The purpose of this feature is to show my data as chart :
DonutModule class, you can see here I tried to add the #Name annotation trying to resolve my problem :
#Module
public class DonutModule {
#Provides
public DonutFragmentMVP.Presenter providedDonutFragmentPresenter(DonutFragmentMVP.Model donutModel) {
return new DonutPresenter(donutModel);
}
#Provides
public DonutFragmentMVP.Model provideDonutFragmentModel(Repository repository) {
return new DonutModel(repository);
}
#Singleton
#Provides
#Named ("donutRepo")
public Repository provideRepo(PollutionApiService pollutionApiService) {
return new CommonRepository(pollutionApiService);
}
}
This package contains a Fragment where I inject the repository in onActivityCreated(). Here I call the method injectDonut() implemented in my App class (the class I shew you just above) :
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((App) getActivity().getApplication()).getComponent().injectDonut(this);
}
==== A common package containing my Repository
public class CommonRepository implements Repository {
private PollutionApiService pollutionApiService;
public CommonRepository(PollutionApiService pollutionApiService) {
this.pollutionApiService = pollutionApiService;
}
#Override
public Observable<Aqicn> getDataFromNetwork(String city, String authToken) {
Observable<Aqicn> aqicn = pollutionApiService.getPollutionObservable(city, authToken);
return aqicn;
}
}
This is a screenshot of my architecture if that can help you to point better how to solve this. Let me know if you need more source code. Thank you.

As long as you provide #Named Repository, you also need to ask for #Named Repository
#Provides
public DonutFragmentMVP.Model provideDonutFragmentModel(#Named("donutRepo") Repository repository) {
return new DonutModel(repository);
}

Related

Dagger - Getting Same Instance On Different Component

I'm having a similar problem like the one in this question.
While the accepted answer does help, but I'm missing final piece to solve the problem.
I have 2 android library modules: common and exp which depends on common.
Everything under common:
#Module
public class CommonModule {
#Singleton
#Provides
public Repository providesRepository() {
return new Repository();
}
}
#Singleton
#Component(modules={CommonModule.class})
public interface CommonComponent {
void inject(CommonClass commonClass);
/**
CommonClass needs instance of Repository
**/
}
public class CommonDIHolder {
public static CommonComponent sComponent;
public static void init() {
sComponent = DaggerCommonComponent.builder().build();
}
}
Everything under exp:
#Module(includes={CommonModule.class})
public class ExpModule {
#Singleton
#Provides
public ExpResource provideExpResource() {
return new ExpResource();
}
}
#Singleton
#Component(modules={ExpModule.class}, dependencies={CommonComponent.class})
public interface ExpComponent {
void inject(ExpClass expClass);
/**
ExpClass needs instance of Repository and ExpResource
**/
}
public class ExpDIHolder {
public static ExpComponent sComponent;
public static void init() {
sComponent = DaggerExpComponent.builder()
.commonComponent(CommonDIHolder.sComponent)
.build();
}
}
I need both CommonClass and ExpClass receive the same instance of Repository.
The problem with this approach is that #Singleton can't depends on #Singleton. So I have to change the scope of ExpComponent into self-defined scope called #ExpScope. Then I changed the provideExpResource into #ExpScope as well.
Then I encountered an error saying that ExpComponent may not reference bindings with different scopes. It refers to the provideRepository which has different scope (#Singleton) on it. If I changed the scope into ExpScope then the CommonComponent will have different scope with provideRepository.
If I changed all #Singleton into #ExpScope then I receive this error message: depends on scoped components in a non-hierarchical scope ordering
What should I do? Or I'm doing the wrong approach here?
Use one and only one #Singleton scoped component
You should have one and only one #Singleton scoped component like this:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
}
Only specify Activities, Fragments, and Services as explicit injection targets for Components
In an Android app, you should only list Activities, Fragments and Services as injection sites. You should configure Dagger 2 to inject the rest of your dependencies without having to resort to calling component.inject(this) inside them.
For example, if your CommonClass looks like this:
public class CommonClass {
#Inject Repository repository;
public class CommonClass() {
CommonComponentHolder.get().inject(this);
}
}
Refactor it like this:
public class CommonClass {
private final Repository repository;
#Inject
public class CommonClass(Repository repository) {
this.repository = repository;
}
}
Now when you have an Activity or Fragment that needs CommonClass and you are injecting with CommonComponent or one of its sub-components or dependent components, they can obtain instances of CommonClass wired with the correct dependencies:
public class MyActivity extends AppCompatActivity {
#Inject CommonClass commonClass;
public void onCreate(Bundle savedInstanceState) {
CommonComponentHolder.getComponent().inject(this);
}
}
Use subcomponents or dependent components to specify the injection targets
Now you have a #Singleton scoped component, you'll probably want to create a component for a narrower scope for your Activity or Fragment. You'll have to connect it to your CommonComponent, so use dependent components or subcomponents (subcomponents are preferred as of Dagger 2.10). Since you say you have already tried defining a #ExpScope, I think the missing piece is to make subcomponent or dependent component with the #ExpScope that injects your Activity or Fragment.
Something like the following for the top-level singleton component:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
ExpComponent.Builder exComponent();
}
And then for the subcomponent:
#ExpScope
#Subcomponent(modules = {NarrowerScopedModule.class})
public interface ExpComponent {
#Subcomponent.Builder
public interface Builder {
Builder narrowerScopedModule(NarrowerScopedModule narrowerScopedModule);
ExpComponent build();
}
}
There are good working examples of Android projects in the Google Android Architecture Blueprints Github repo

Dagger 2 How to create a Module for Base Activity Components and a separate Module for all MVP components

Hello I am new to Dagger2.
Goal. Take my Networking DI and MVP DI. MVP as in the presenter for an an activity that extends base activity. I want to combine all this into one super module and place this into my base activity. Over time add more presenters.
I do not want 30+ inject statements in my baseActivity.
I am following this example but it is too simple compared to what I am trying to do.
I think the issue is with injecting the API at the base activity. For some reason Dagger is looking for Api in my MVP class.. So that would be a dependency graph issue?
Having spent more time on this.. The issue stems from Mvp's interface of baseActivity or any sub activity that extends baseActivity. That means when it goes to inject, it sees the #inject Api call, and cannot find it. It will work if I add Api to this module, but thats upside down of what I want. I want Component / Module for Application level items. I then want a component / module that has all my different MVP component in one module.. It's like Dagger starts looking for dependencies in the leaf of a tree and getting upset when it doesn't see whats in the root. I need it to go the other way. Be satisfied that I injected the dependency in the Root activity.
Base Activity...
#inject
public ApiClient mClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mManager = new SharedPreferencesManager(this);
DaggerInjector.get().inject(this);
}
DaggerInjector
public class DaggerInjector {
private static AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
public static AppComponent get() {
return appComponent;
}
}
#Component(modules = {AppModule.class, ApiModule.class, MvpModule.class})
#Singleton
public interface AppComponent {
void inject(BaseActivity activity);
}
Api
#Singleton
#Component(modules = {ApiModule.class})
public interface ApiComponent {
void inject( BaseActivity activity);
}
#Module
public class ApiModule {
#Provides
#Singleton
public ApiClient getApiClient(){
return new ApiClient();
}
}
Mvp
#Singleton
#Component(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
Error:(16, 10) error: ApiClient cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method. This type supports members injection but cannot be implicitly provided.
ApiClient is injected at
...BaseActivity.ApiClient
...BaseActivity is injected at
MvpComponent.inject(activity)
I found out my problem. I needed to use a subcomponent.
#Singleton
#Subcomponent(modules = {MvpModule.class})
public interface MvpComponent {
void inject(BaseActivity activity);
}
#Module
public class MvpModule {
#Provides
#Singleton
public MvpPresenter getMvpPresenter(){ return new MvpPresenter();}
}
see
Dagger- Should we create each component and module for each Activity/ Fragment
Dagger2 activity scope, how many modules/components do i need?

Dagger 2 error: "RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method"

For example, I have following interface:
public interface Repository {
Observable<Pojo> getPojos();
}
And its implementation:
public class RepositoryImpl implements Repository {
public RepositoryImpl() {
}
#Override
public Observable<Pojo> getPojos() {
return null;
}
}
Module:
#Module
class AppModule {
public AppModule() {
}
#Provides
#Singleton
Repository provideRepositoryImpl() {
return new RepositoryImpl();
}
}
And component:
#Singleton
#Component(modules = { AppModule.class })
public interface AppComponent {
void inject(MainActivity mainActivity);
}
When I trying to build project, I receive error as in question title. What problem in my code?
Read your error carefully (emphasis mine):
Dagger 2 error: “RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method”
Generally this means you've tried to #Inject RepositoryImpl, not #Inject Repository. This is especially important because your Module directly calls the RepositoryImpl constructor rather than letting Dagger create your RepositoryImpl using an #Inject-annotated constructor. (If you had, you could make RepositoryImpl a parameter of your #Provides method or switch to a #Binds method, and you have your choice between injecting the interface versus the implementation.)
The way I setup Dagger 2 is in my projects Application I add the injection component. like so.
public class NyApplication extends Application {
InjectionComponent component;
#Override
public void onCreate() {
super.onCreate();
setDagger();
}
private void setDagger() {
component = DaggerAppComponent.builder()
.appComponent(new AppModule())
.build();
component.inject(this);
}
public InjectionComponent getComponent() {
return component;
}}
and then int my activity whatever it is. I inject on it's onCreate like this.
public class MainActivity extends Activity {
#Inject
Object object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).getComponent().inject(this);
}}
I hope this helps you.

Adding Non Activity Classes to Dagger 2 Graph Android

I'm having a hard time wrapping my head around how to use Dagger 2.0 outside of the limited examples I've seen. Let's take an example reading application. In this reading app, there is a library of a user's stories and the ability to Log in. The classes of interest for the purpose of this example are:
MainApplication.java - extends Application
LibraryManager.java - Manager which is responsible for adding/removing stories in the user's library. This is called from the MainApplication
AccountManager.java - Manager which is responsible for saving all a user's login information. It can be called from the LibraryManager
I'm still trying to wrap my head around what Components and Modules I should be creating. Here's what I can gather so far:
Create a HelperModule that provides an AccountManager and LibraryManager instance:
#Module
public class HelperModule {
#Provides
#Singleton
AccountManager provideAccountManager() {
return new AccountManager();
}
#Provides
#Singleton
LibraryManager provideLibraryManager() {
return new LibraryManager();
}
}
Create a MainApplicationComponent that lists the HelperModule in its list of modules:
#Singleton
#Component(modules = {AppModule.class, HelperModule.class})
public interface MainApplicationComponent {
MainApplication injectApplication(MainApplication application);
}
Include #Injects LibraryManager libraryManager in the MainApplication and inject the application into the graph. Finally it queries the injected LibraryManager for the number of stories in the library:
public class MainApplication extends Application {
#Inject LibraryManager libraryManager;
#Override
public void onCreate() {
super.onCreate();
component = DaggerMainApplicationComponent.builder()
.appModule(new AppModule(this))
.helperModule(new HelperModule())
.build();
component.injectApplication(this);
// Now that we have an injected LibraryManager instance, use it
libraryManager.getLibrary();
}
}
Inject the AccountManager into the LibraryManager
public class LibraryManager {
#Inject AccountManager accountManager;
public int getNumStoriesInLibrary() {
String username = accountManager.getLoggedInUserName();
...
}
}
However the problem is that the AccountManager is null when I try to use it in the LibraryManager and I don't understand why or how to solve the problem. I'm thinking that it's because the MainApplication that was injected into the graph doesn't use the AccountManager directly, but then do I need to inject the LibraryManager into the graph some how?
modify your classes as follow and it would work:
your POJO:
public class LibraryManager {
#Inject AccountManager accountManager;
public LibraryManager(){
MainApplication.getComponent().inject(this);
}
public int getNumStoriesInLibrary() {
String username = accountManager.getLoggedInUserName();
...
}
...
}
your component Interface:
#Singleton
#Component(modules = {AppModule.class, HelperModule.class})
public interface MainApplicationComponent {
void inject(MainApplication application);
void inject(LibraryManager lm);
}
}
your application Class :
public class MainApplication extends Application {
private static MainApplicationComponent component;
#Inject LibraryManager libraryManager;
#Override
public void onCreate() {
super.onCreate();
component = DaggerMainApplicationComponent.builder()
.appModule(new AppModule(this))
.helperModule(new HelperModule())
.build();
component.injectApplication(this);
// Now that we have an injected LibraryManager instance, use it
libraryManager.getLibrary();
}
public static MainApplicationComponent getComponent(){return component;}
}
In fact, you need to do the same for all of your dependent classes, basically you have access to the application class in all Activity sub-classes so making get component as an static method is none-less. but for POJO u need to catch the component somehow. there are a lot of way to implement. this is just an illustration to give u the idea how it works.
now you can destroy the mars :)
You can satisfy dependency directly in provide method:
#Provides
#Singleton
LibraryManager provideLibraryManager(AccountManager accountManager) {
return new LibraryManager(accountManager);
}
Or use constructor injection (remove provideLibraryManager() method from HelperModule):
#Signleton
public class LibraryManager {
private final AccountManager accountManager;
#Inject
public LibraryManager(AccountManager accountManager) {
this.accountManager = accountManager
}
public int getNumStoriesInLibrary() {
String username = accountManager.getLoggedInUserName();
...
}
}
Objects created with constructor injection are provided automatically.
If you have a lot of parameters in LibraryManager you can use method injection for setters in addition to constructor injection:
#Singleton
public class LibraryManager {
private final AccountManager accountManager;
private SomeManager someManager;
#Inject
public LibraryManager(AccountManager accountManager) {
this.accountManager = accountManager
}
#Inject
public setSomeManager(SomeManager someManager) {
this.someManager = someManager
}
public int getNumStoriesInLibrary() {
String username = accountManager.getLoggedInUserName();
...
}
}
Method injection is performed after object is instantiated. However, this use case of method injection is not valid, try to prefer constructor or field injection.
I think I've come up with a pretty good solution. Instead of trying to inject the AccountManager into the LibraryManager, I'm providing the AccountManager in the MainApplicationComponent and accessing from the LibraryManager that way.
MainApplicationComponent:
#Singleton
#Component(modules = {AppModule.class, HelperModule.class})
public interface MainApplicationComponent {
MainApplication injectApplication(MainApplication application);
// Provide the managers here so all classes that have a pointer to the MainApplicationComponent can access them.
// This avoids having to pass each manager to the constructor of all classes that need them
AccountManager accountManager();
ArchiveManager archiveManager();
}
Using the sample Android App for inspiration (https://github.com/gk5885/dagger-android-sample) I've created a HasComponent interface:
public interface HasComponent<C> {
C getComponent();
}
and made the MainApplication implement the interface. Also when creating the HelperModule you'll notice it passes this so the module can access the component:
public class MainApplication extends Application implements HasComponent<MainApplicationComponent>{
MainApplicationComponent mainApplicationComponent;
#Override
public MainApplicationComponent getComponent() {
return mainApplicationComponent;
}
#Override
public void onCreate() {
super.onCreate();
component = DaggerMainApplicationComponent.builder()
.appModule(new AppModule(this))
.helperModule(new HelperModule(this))
.build();
component.injectApplication(this);
// Now that we have an injected LibraryManager instance, use it
mainApplicationComponent.libraryManager().getLibrary();
}
}
The LibraryManager is changed so it takes the HasComponent in as a parameter in the constructor:
public class LibraryManager {
AccountManager accountManager;
public ArchiveManager(HasComponent<MainApplicationComponent> hasComponent) {
accountManager = hasComponent.getComponent().accountManager();
}
...
}
and finally in the HelperModule we just pass the implementation of HasComponent<MainApplicationComponent> to the LibraryManager's constructor:
#Module
public class HelperModule {
private HasComponent<WattpadComponent> hasComponent;
public HelperModule(HasComponent<WattpadComponent> hasComponent) {
this.hasComponent = hasComponent;
}
#Provides
#Singleton
AccountManager provideAccountManager() {
return new AccountManager(hasComponent);
}
#Provides
#Singleton
ArchiveManager provideLibraryManager() {
return new LibraryManager(hasComponent);
}
}
This should also make it really easy for unit testing. If I am unit testing the LibraryManager and want to mock out the AccountManager I can simply create a TestMainApplicationComponent that extends MainApplicationComponent and includes a TestHelperModule in it's list of modules which will provide a mocked AccountManager and pass the TestMainApplicationComponent to the LibraryManager's constructor.
I'm new to Dagger so I might be missing something but I've tried out everything but the unit testing and it seems to be working so far. Will post a GitHub link shortly with unit testing examples for those interested.
Thanks to #Kirill's answer for a better understanding of how the Components instantiate the objects.

Dagger v2: Inject 2 different scopes into one object

I have moduleA setup as an application wide singleton provider, ModuleB as a user related object provider
My user display fragment will use system wide bus to send message to others and use user related object to display.
Problem cannot inject different scrope class into one object. Use component.getX method works fine, but inject is prefered way.
Error message:
#UserScope may not reference bindings with difference scopes: #Provides #Singleton Bus ModuleA.provideBus()
#Module
public class ModuleA {
#Provides #Singleton Bus provideBus() {...}
}
Module B as user related Info provider
#Module
public class ModuleB{
private final User user;
public ModuleB(User user) {...}
#Provides #UserScope User provideUser() {}
#Provides #UserScope UserManager provideUserManager() {}
}
Components setup like following:
#Component (modules={ModuleA.class})
#Singleton
public interface ComponentA {
Bus getBus();
void inject(ClassA target);
}
#Component(modules={ModuleB.class})
#UserScope
public interface ComponentB {
User getUser();
UserManager getUserManager();
void inject(ClassA target);
}
class UserFragment exrtends Fragment {
#Inject Bus bus;
#Inject UserManager userManager;
public void onCreate() {
getComponentA().inject(this);
getComponentB().inject(this);
}
}
Try this configuration, it works for me. There is really a lack of good documentation about Dagger2 so I studied a few open-source examples of code that you can find in GitHub etc by keyword like Dagger2.
Application level Component
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
// exported for child-components
Bus eventBus();
}
Application level Module
#Module
public class AppModule {
#Provides #Singleton
Bus provideBus() {
return BusProvider.getInstance();
}
}
Activity level Component
#ActivityScope
#Component(dependencies=AppComponent.class, modules=MainActivityModule.class)
public interface MainActivityComponent {
void inject( MainActivity mainActivity );
}
Activity level Module
#Module
public class MainActivityModule {
private final MainActivity mActivity;
public MainActivityModule( MainActivity activity ) {
mActivity = activity;
}
#Provides
MainActivityTitleController provideTitleController() {
return new MainActivityTitleController( mActivity );
}
}
Android Application class
public class MyApplication extends Application {
private AppComponent mAppComponent;
#Override
public void onCreate() {
super.onCreate();
// Dagger2
mAppComponent = Dagger_AppComponent.builder()
.appModule( new AppModule( this ))
.build();
}
public AppComponent getComponent() {
return mAppComponent;
}
public static AppComponent getComponent( Context context ) {
return ((MyApplication)context.getApplicationContext()).getComponent();
}
}
And finally Activity
public class MainActivity extends ActionBarActivity {
// Injectable fields
#Inject Bus mEventBus;
#Inject MainActivityTitleController mTitleController;
private MainActivityComponent mComponent;
#Override
protected void onCreate( Bundle savedInstanceState ) {
// Dagger2
mComponent = Dagger_MainActivityComponent.builder()
.appComponent( ((MyApplication)getApplication()).getComponent() )
.mainActivityModule( new MainActivityModule( this ) )
.build();
mComponent.inject( this );
}
}
I think the main problem in your code snippets you provided, is that your ModuleB should have a dependency on ModuleA to correctly provide the singleton with the error you were getting. I.e. this should work:
#Component(modules={ModuleB.class}, dependencies = ComponentA.class)
#UserScope
public interface ComponentB {
User getUser();
UserManager getUserManager();
void inject(MainActivity target);
}
I recreated your classes and made some assumptions to fill in the blanks, and it seems to work fine. You can see the full working code here on GitHub. The only difference in my code is, what you called ClassA/UserFragment I just called MainActivity but otherwise the structure is the same.

Categories

Resources