How to use dagger2 subcomponent? - android

According to official documents:https://google.github.io/dagger/subcomponents.html ,I add a subcomponent in #Module, like this:
#Module(subcomponents = {MainActivityComponent.class})
public class ContextModule {
private Context mContext;
public ContextModule(Context context) {
mContext = context;
}
#Provides
public Context provideContext() {
return mContext;
}
}
And declare my component and subcomponent like this:
#Component(modules = ContextModule.class)
public interface AppComponent {
Context provideContext();
MainActivityComponent getMainActivityComponent();
}
#Subcomponent(modules = {HardwareModule.class, SoftwareModule.class})
public interface MainActivityComponent {
void injectMainActivity(MainActivity activity);
}
But the code can not be compiled successfully. The error is this:
Error:(11, 1) : com.kilnn.dagger2.example.MainActivityComponent doesn't have a #Subcomponent.Builder, which is required when used with #Module.subcomponents
I don't know how to write a #Subcomponent.Builder , and if i remove the subcomponent declare in #Module, everything is ok. So i don't know what is the right way to use subcomponent.

Actually, the error is quite descriptive, all you need to do is add the Builder to your Subcomponent like this:
MainActivityComponent.class
#Subcomponent.Builder
interface Builder {
MainActivityComponent build();
}
For your current implementation, and since you don't have special dependencies you don't really need the Subcomponent.
Note: For convention's sake I recommend you to rename your Subcomponent to MainActivitySubcomponent

Related

Dagger 2 how to perform constructor injection

I have a class
public class DialogUtils
{
private Context context;
#Inject
public DialogUtils(Context context)
{
this.context = context;
}
}
In my activity class i have did but i'm getting null pointer exception on dialogUtils instance.
public class LoginActivity extends Activity{
#Inject DialogUtils dialogUtils;
}
I know how to inject dependency via module and component but not sure how to with construction injection. Any help is much appreciated.
If you don't retain the activity-level component and you aren't inheriting from a superscope (application-level component) using component dependency or subcomponent, then it's the following
// unscoped
public class DialogUtils {
private final Context context;
#Inject
public DialogUtils(Context context) {
this.context = context;
}
}
then
#Module
public class ActivityModule {
private final Context context;
public ActivityModule (Context context) {
this.context = context;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return context;
}
}
#Component(modules={ActivityModule.class})
#Singleton
public interface ActivityComponent {
Context context();
DialogUtils dialogUtils();
void inject(MainActivity mainActivity);
}
And then
#Inject
DialogUtils dialogUtils;
...
ActivityComponent activityComponent = DaggerMainActivityComponent.builder()
.activityModule(new ActivityModule(MainActivity.this))
.build();
activityComponent.inject(this); // activityComponent.dialogUtils() also works
On the one hand you are registering DialogUtils for Constructor Injection, so any component could provide it.
On the other hand an activity and other parts of the Android Framework still need to be field injected. Dagger can't call their constructor, and #Inject DialogUtils dialogUtils; will not just magically appear.
Here you have to use a component, and register a method that takes your components type as an argument. Dagger will then create the method to inject your activities fields.
#Component MyComponent {
inject(LoginActivity activity);
}
To inject the fields you still have to create your component, and call the inject(loginActivity) method.
void onCreate(...) {
MyComponent component = // create the component
// dagger does some heavy lifting here
component.inject(this);
dialogUtils.doSomething(); // not null, we just injected our fields
}

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 2: Unable to inject singleton in other scope

I have Singleton scoped module that provides some standard singletons: Application, DB services, etc.
But for Activity I have separate module that should create Presenter for he Activity and I need to pass Application context to it. However I get following error when trying to compile the project:
Error:(13, 1) error: xxx.SplashComponent scoped with #xxx.ViewScope may not reference bindings with different scopes:
#Provides #Singleton xxx.ApplicationModule.provideAppContext()
Here is snippet of my Application module:
#Singleton
#Module
public class ApplicationModule {
private Application app;
public ApplicationModule(Application app) {
this.app = app;
}
#Provides
#Singleton
#Named("ui")
Scheduler provideUIScheduler() {
return AndroidSchedulers.mainThread();
}
#Provides
#Singleton
#Named("io")
Scheduler provideIOScheduler() {
return Schedulers.io();
}
#Provides
#Singleton
Application provideApplication() {
return app;
}
#Provides
#Singleton
Context provideAppContext() {
return app;
}
}
And here is Activity module and Component:
#Module
public class SplashModule {
private final FragmentManager fragmentManager;
public SplashModule(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
#Provides
#ViewScope
Presenter getPresenter(Context context) {
return new SplashPresenter(context, fragmentManager);
}
}
Component:
#ViewScope
#Component(modules = {SplashModule.class, ApplicationModule.class})
public interface SplashComponent {
void inject(SplashActivity activity);
}
What am I doing wrong?
What am I doing wrong?
This:
#ViewScope
#Component(modules = {SplashModule.class /*View scoped*/,
ApplicationModule.class/*Singleton scoped*/})
You can only include unscoped or modules scoped with the same scope in your components. You will need to use more than one component.
To include the dependencies from your application, you need to have them in a different component, e.g. ApplicationComponent. If yo do this, you have 2 options: either declare SplashComponent as a SubComponent of ApplicationComponent or add ApplicationComponent as a dependency to your component. If you add it as a dependency, be sure to also provide methods in your ApplicationComponent, so that it can access the dependencies.
e.g. if you were to use component dependencies:
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MyApplication app);
// todo: also add getters for your other dependencies you need further down the graph
Application getApplication();
}
#Component(modules = {SplashModule.class}, dependencies={ApplicationComponent.class})
public interface SplashComponent {
// as before
}
I want to explain some key points of Dagger 2 from my understanding.
Main actors:
"Component" is the bridge between modules and places where injection happens.
"Module" is the place where we declare our objects which will be injected.
"Scope" is like the life-time of related injection story.
How does it work?
Declare component with a scope ("Singleton").
Define modules that can inject the required objects in the component's modules list.
void inject(BaseFragment baseFragment);
*******Expose the provided objects in the component's module to sub components****
DbHelper dbHelper();
Declare module and provides objects to be injected via component.
Ex:
#Singleton
#Component(modules = { ApplicationModule.class, NetworkModule.class })
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
DbHelper dbHelper();
}
#PerService #Component(dependencies = ApplicationComponent.class, modules = ServiceModule.class)
public interface ServiceComponent {
void inject(SyncService service);
}
// SyncService.java
#Inject DbHelper dbHelper; (even Singleton scoped)
private void setupInjector() {
ServiceComponent mServiceComponent = DaggerServiceComponent.builder()
.applicationComponent(getApplicationComponent())
.serviceModule(new ServiceModule(this))
.build();
mServiceComponent.inject(this);
}
ok then...
You can inject both unscoped and (Singleton and PerService) scoped objects to your SyncService.class
Scoping rules:
When a type is marked with a scope annotation, it can only be used by Components that are annotated with the same scope.
When a Component is marked with a scope annotation, it can only provide types with that annotation or types that have no annotation.
A subcomponent cannot use a scope annotation used by one of its parent Components.
Components also involve subcomponents in this context.
Caution:- Modules that use a scope annotation can only be used in components that are annotated with the same scope. Check Here.
For more information on Dagger in Android go through this training and practise it here.

Dagger 2 - modules from different components

I am not quite sure how to solve this with dagger 2.
Lets assume we have ApplicationModule that provides us ApplicationContext
then we have ApplicationComponent that uses just this one module.
Then on top of it we have ActivityModule and ActivityComponent that has dependency on ApplicationComponent.
ActivityComponent is build just like
ApplicationComponent component = ((MyApplication) getApplication()).getComponent();
mComponent = Dagger_ActivityComponent.builder()
.applicationComponent(component)
.activityModule(new ActivityModule(this))
.build();
And then I inject my activity:
mComponent.inject(this);
Now I am able to use everything that is declared inside my ActivityModule, however it is not possible for me to access ApplicationModule.
So the question is how could that be achieved? So that when I build component that depends on another component I can still access module from the first one?
EDIT
I think I have found solutions, after rewatching Devoxx talk by Jake again, I have had to miss that out, whatever I want to use from another components module I have to provide in that component, for example I want to use Context from ApplicationModule then inside ApplicationComponent I have to state Context provideContext(); and it is going to be available. Pretty cool :)
You have already answered your question, but the answer is to specify the provision methods in your "superscoped" component (ApplicationComponent).
For example,
#Module
public class ApplicationModule {
#Provides
#Singleton
public Something something() {
return new Something.Builder().configure().build();
// if Something can be made with constructor,
// use #Singleton on the class and #Inject on the constructor
// and then the module is not needed
}
}
#Singleton
#Component(modules={ApplicationModule.class})
public interface ApplicationComponent {
Something something(); //PROVISION METHOD. YOU NEED THIS.
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
#ActivityScope
public class OtherThing {
private final Something something;
#Inject
public OtherThing(Something something) {
this.something = something;
}
}
#Component(dependencies = {ApplicationComponent.class})
#ActivityScope
public interface ActivityComponent extends ApplicationComponent { //inherit provision methods
OtherThing otherThing();
}

Categories

Resources