How to #Provide services with Dagger 2 - android

I'm using Dagger 2 for DI in my MVP application. The application has different services which are responsible for making requests like getting product data or doing an order.
Most of the time each screen has his own #Component interface and #Module class and in these modules the corresponding presenters are provided.
In some cases a service is only used by one presenter. In this case I just #Provide it in that module. But I have a lot of services which are used multiple times in different presenters. At the moment I have put them all in the ApplicationModule, expose them in the ApplicationComponent and add a dependency to ApplicationComponent in the 'screen components' if their presenters need one of the services. Now the problem is, that almost every component is depending on ApplicationComponent, which I think is wrong.
How can I handle this the best way? Should I create a module for each service and add it to the components that need it? I also want to keep scoping in mind and think the services should be added to the #Singleton scope.
ApplicationComponent
#Singleton
#Component(modules = {ApplicationModule.class, ContextModule.class}
public interface ApplicationComponent {
void inject(MyApplication application);
Context getContext();
ProductDataService getProductDataService();
AnalyticsService getAnalyticsService();
CartService getCartService();
...
}
ApplicationModule
#Module
public class ApplicationModule {
#Provides
static ProductDataService provideProductDataService(ProductDataServiceImpl productDataService) {
return productDataService;
}
#Provides
static AnalyticsService provideAnalyticsService(AnalyticsServiceImpl analyticsService) {
return analyticsService;
}
#Provides
static CartService provideCartService(CartServiceImpl cartService) {
return cartService;
}
...
}
ProductDataServiceImpl
#Singleton
public class ProductDataServiceImpl implements ProductDataService {
private WebService webService;
#Inject
public ProductDataServiceImpl(WebService webService) {
this.webService = webService;
}
...
}
DependentComponent
#Component(modules = DependentModule.class, dependencies = ApplicationComponent.class)
public interface DependentComponent {
void inject(MyFragment fragment)
}
DependentModule
#Module
public class DependentModule {
private MyView myView;
public DependentModule(MyView myView) {
this.myView = myView;
}
#Provides
public MyPresenter provideMyPresenter(ProductDataService productDataService, AnalyticsService analyticsService) {
return new MyPresenterImpl(myView, productDataService, analyticsService)
}
}

Related

Unable to inject livedata repository class using Dagger2

I have an Android project which is using Dagger 2, Livedata and viewmodel, rxjava and databinding.
I have created an AppComponent and two SubComponents as of now. The Appcomponent is for applicationscope and one Subcomponent that is ControllerComponent is for Activity, fragments and dialogs. This ControllerComponent is injected in the baseactivity and it is working fine.
The other Sub component is RetrofitComponent which is for viewmodel repositories. Now I can't seem to find a way to inject the retrofit component in the base repository since it is a Subcomponent and I don't have the applicationcontext in the repository class.
AppComponent:
#ApplicationScope
#Component(modules = {
ApplicationModule.class,
PrefModule.class
})
public interface AppComponent {
public void inject(AppInstance appInstance);
public AppPreference getAppPref();
public Application getApplication();
public ControllerComponent newControllerComponent(ControllerModule controllerModule);
public RetrofitComponent newRetrofitComponent(RetrofitModule retrofitModule);
}
RetrofitComponent:
#RepositoryScope
#Subcomponent(modules = {RetrofitModule.class})
public interface RetrofitComponent {
public void inject(BaseRepo baseRepo);
public APICall getApiCalls();
}
RetrofitModule:
#Module
public class RetrofitModule {
#Provides
#RepositoryScope
public APICall providePostApi(Retrofit retrofit) {
return retrofit.create(APICall.class);
}
#Provides
#RepositoryScope
public Retrofit provideRetrofitInterface() {
return new RetrofitService().retrofit;
}
}
BaseRepo;
#Inject
public APICall apiCall;
private Disposable subscription;
CompositeDisposable compositeDisposable = new CompositeDisposable();
private RetrofitComponent injector;
public BaseRepo() {
//injector = DaggerRetrofitComponent.builder().retrofitModule(new RetrofitModule()).build();(I tried with dependent component had different problem)
injector = ((AppInstance)getApplication()).getAppComponent().newRetrofitComponent(new RetrofitModule());(Not able to use getApplication())
injector.inject(this);
}
I'm going to use the child repositories extending this base repository from their corresponding viewmodels so I don't intend to pass on the application from the activities or fragments.
What I might be missing? Or doing wrong? Or is my approach wrong?
And one more thing once this is done, should I inject the repository classes in the Viewmodel class or should I create a factory class and call whichever repository I need, which is better practice?

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?

How can Dagger 2 be used to inject using multiple components into the same object

So I have an ApplicationComponent for injecting singletons into my fragments and presenters, but I'm trying to create a component to inject into the same presenter that the AppComponent does. Something along these lines.
#Component{modules = FileManagerModule.class}
public interface FileManagerComponet
{
public void inject(MyPresenter presenter);
}
#Component{modules = AppModule.class}
public interface AppComponent
{
public void inject(MyPresenter presenter);
}
#Module
public class AppModule
{
private Context appContext;
#Provides
#Singleton
public SharedPreferences preferences()
{
return appContext.sharedPreferences();
}
...
}
#Module
public class FileManagerModule
{
private Context activityContext;
#Provides
public FileManager FileManager()
{
return new FileManager(activityContext);
}
...
}
To anyone who can't figure this out, one component must provide all the dependencies to an object. So in my case, I'd have to make the FileManagerComponent be a Subcomponent and ".plus()" it with my AppComponent, or make it dependent on AppComponent and have AppComponent expose Context downstream by having a Context context(); Method that will let components that depend on it have access to a the context it has.
For example:
#Singleton
#Component(modules = {NetworkModule.class, AndroidModule.class})
public interface ApplicationComponent {
FileManagerComponent plus(FileManagerModule module);
}
#Subcomponent(modules = {FileManagerModule.class})
public interface FileManagerComponent {
void injectMyActivity(MyFileManagingActivity activity);
}
And you would use it like this (in MyFileManagingActivity):
FileManagerComponent fmc = applicationComponent.plus(new FileManagerModule());
fmc.injectMyActivity(this);
Or if you don't want to use subcomponents something like this:
#Singleton
#Component(modules = {NetworkModule.class, AndroidModule.class})
public interface ApplicationComponent {
Context context();
File applicationRootDirectory();
}
// Notice this is ALSO a Component
#Component(modules = {FileManagerModule.class}, dependencies = ApplicationComponent.class)
public interface FileManagerComponent {
void inject(MyFileManagerActivity activity);
}
Now you have to build your component that depends on app component.
FileManagerComponent fmc = DaggerFileManagerComponent.builder()
.applicationComponent(appComponent)
.fileManagerModule(new FileManagerModule())
.build();
fmc.inject(this);

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