Dagger 2 how to perform constructor injection - android

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
}

Related

How to inject Context instance into the type converter?

I have a type converter like:
class DateConverter {
Context mContext;
public DateConverter(Context context) {
mContext = context;
}
#TypeConverter
public Foobar toFoobar(String str) {
return App.get(mContext).getComponent()
.getFoobarManager().convert(str);
}
}
The problem for me is that I have no idea to inject context into the DateConverter instance.
PS:
the project is using dagger2, so I prefer inject instead of reference to a static Context instance.
Thanks in advance!
Change your constructor to public DateConverter() as context we will add through dagger2.
If you have used dagger2 in your project there must be any component class. Component is basically an interface in application which lets views inject through dagger. There must be some methods which having name inject() with different parameters for example
public abstract void inject(MainActivity activity);
public abstract void inject(DaggerApplication daggerApplication);
create your own method there for your DateConverter
public abstract void inject(DateConverter dateconverter);
Now add use this inject method in your DateConverter , the way you have used it in other classes. Also define below code in your DateConverter
#Inject
Context context;

How to use dagger2 subcomponent?

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

How to inject into a java class that doesn't have any activity or fragment using dagger2

Android Studio 2.2.2
I have a NewsListModelImp class which is the model in the MVP.
I want to inject my retrofit service into the model. However, as NewsListModelImp doesn't contain any reference to a context or activity I cannot call getApplication(). Which is what you would do if you were in a activity or fragment. I don't want to pass any context or activity in the constructor of NewsListModeImp as that would have to come from the presenter and I want to avoid any android stuff there.
public class NewsListModelImp implements NewsListModelContract {
#Inject
NYTimesSearchService mNYTimesSearchService;
public NewsListModelImp() {
((NYTimesSearchApplication)getApplication()).getAppComponent().inject(this);
}
}
My Application class
public class NYTimesSearchApplication extends Application {
private AppComponent mAppComponent;
public void onCreate() {
super.onCreate();
/* Setup dependency injection */
createAppComponent();
}
private void createAppComponent() {
mAppComponent = DaggerAppComponent
.builder()
.retrofitModule(new RetrofitModule())
.build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
My provides module
#Module
public class RetrofitModule {
private Retrofit retrofit() {
return new Retrofit
.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
#Provides
#Singleton
public NYTimesSearchService providesNYTimesSearch() {
return retrofit().create(NYTimesSearchService.class);
}
}
My Appcomponent
#Singleton
#Component(modules = {RetrofitModule.class})
public interface AppComponent {
void inject(NewsListModelImp target);
}
Many thanks for any suggestions,
Dagger-2 works reccurently. So if inside Activity (or Fragment) object is injected and it's constructor is properly annotated with #Inject annotation, the constructor's parameters will be injected too.
Suppose inside the application you would like to inject:
#Inject NyTimesPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((NYTimesSearchApplication) getApplication()).getAppComponent().inject(this);
}
The constructor of NyTimesPresenter must be annotated with #Inject:
public class NyTimesPresenter {
NewsListModelImp newsListModel;
#Inject
public NyTimesPresenter(NewsListModelImp newsListModel) {
this.newsListModel = newsListModel;
}
}
NewsListModelImp constructor must also be annotated with #Inject:
public class NewsListModelImp implements NewsListModelContract {
NYTimesSearchService mNYTimesSearchService;
#Inject
public NewsListModelImp(NYTimesSearchService nYTimesSearchService) {
this.mNYTimesSearchService = nYTimesSearchService;
}
}
Then everything will be injected properly.
Why the parameters should be passed to class as constructors' parameters? Such design pattern conforms SOLID principles. Object dependencies are injected into objects and not created inside it and such code is easily testable (in tests dependencies can be replaced ie. by Mock's)
EXTRA INFO:
It is possible to inject objects implementing specific interfaces. Such technique is described here. In your case NyTimesPresenter can have NewsListModelContract as it's dependency instead of NewsListModelImp. To do this add another module to your AppComponent:
#Singleton
#Component(
modules = {
RetrofitModule.class,
AppModule.class
})
public interface AppComponent {
AppComponent method to provide concrete class implementing interface should look like:
#Singleton
#Module
public abstract class AppModule {
#Binds
public abstract NewsListModelContract provideNewsListModelContract(NewsListModelImp newsListModelImp);
}
The implementation of NyTimesPresenter should change (just to replace concrete class with interface it implements):
public class NyTimesPresenter {
NewsListModelContract newsListModel;
#Inject
public NyTimesPresenter(NewsListModelContract newsListModel) {
this.newsListModel = newsListModel;
}
}

Android Test with Dagger mock inject constructor

Hi i've got a following Problem. I want to write android tests with espresso for the Ui and in order to have tests that are not flaky i want to mock my presenter.
I use Dagger in the App. My Configuration is as Following:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
//some injections
//some providings
}
I have a Module for the Component
#Module
public class AppModule {
//providings for component
}
then i have also a component for the activities with a module for the component
#PerActivity
#Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
//inject activites
//provide subcomponents for activites
}
then i have subcomponents for my pages
#PerActivity
#Subcomponent(modules = InfoModule.class)
public interface InfoComponent {
void inject(DetailActivity activity);
}
and a module for the subcomponent
#Module
public class InfoModule {
#Provides
public DetailPresenter provideDetailPresenter(ShowDetailsUseCase showDetailsUseCase,
OtherUseCase getPoisUseCase,
AccountManager accountManager, Navigator
navigator) {
return new DetailPresenter(showDetailsUseCase, otherUseCase, accountManager, navigator);
}
}
and then the detail Activity Injects the DetailPresenter
public class DetailActivity extends BaseActivity {
#Inject
DetailPresenter mPresenter;
InfoComponent mComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mComponent = getActivityComponent().provideInfoModule(new InfoModule());
mComponent.inject(this);
mPresenter.bindView(this);
mPresenter.onCreate(new PresenterBundle(getIntent().getExtras(), savedInstanceState));
}
//functionality of detailActiviy
}
then i have the presenter which uses constructor injection
public class DetailPresenter extends BasePresenter {
private ShowDetailsUseCase mDetailsUseCase;
private final OtherUseCase getPoisUseCase;
private AccountManager accountManager;
private Navigator navigator;
#Inject
public DetailPresenter(ShowDetailsUseCase getDetailsUseCase, OtherUseCase getPoisUseCase,
AccountManager
accountManager, Navigator navigator) {
this.mDetailsUseCase = getDetailsUseCase;
this.getPoisUseCase = gotherUseCase;
this.accountManager = accountManager;
this.navigator = navigator;
}
#Override
public void onCreate(#Nullable PresenterBundle bundle) {
super.onCreate(bundle);
//other things to do on initialization
((DetailView) getView()).showDetails(getDetailsFromUseCase());
}
}
Now in the test i want to do mock the presenter:
#RunWith(AndroidJUnit4.class)
public class DetailActivityTest {
#Rule
public final ActivityTestRule<DetailActivity> main = new ActivityTestRule<DetailActivity>(DetailActivity.class, true, false);
#Rule
public final DaggerMockRule<AppComponent> rule=new EspressoDaggerMockRule();
#Mock
DetailPresenter presenter; //does not work because #Inject constructor
#Test
public void locationTest() {
Details details = generateDetails();
launchActivity();
doAnswer(answer -> {
activity.showDetails(details);
return null;
}
).when(presenter).onCreate(any());
//espresso verify afterwards
}
}
but if i try to mock the following error shows:
java.lang.RuntimeException: Error while trying to override objects:
a.b.c.ui.mvp.presenter.DetailPresenter
You must define overridden objects using a #Provides annotated method instead of using #Inject annotation
does someone have an idea how I am able to mock the presenter even with #Inject constructor and dependencies.
I do not want to mock the data layer because then I have to mock database, apiClient, cacheData and so on. And some of the datalayer also have inject dependencies so i cannot mock them either.
Thank you in advance
The DetailPresenter class is created in the InfoModule, so you don't need the Inject annotation. The error you get is because using DaggerMock you can replace only the objects created in a module. In your example you are already creating it in a module, you just need to remove the Inject annotation.

Is it possible to mix constructor and field injection in Dagger?

I'm trying to use Dagger for my android project but I encounter a Problem: Is it possible to mix constructor and field injection?
I have a module and two classes:
#Module(injects = fooActivity.class)
public class Module {
private fooActivity activity;
public Module(fooActivity activity) {
this.activity = activity;
}
#Provides
public bar provideBar() {
return new bar(activity);
}
}
public class fooActivity extends Activity {
#Inject Baz baz;
public void onCreate(Bundle savedInstanceState) {
...
ObjectGraph graph = ObjectGraph.create(new BasicModule()).plus(new Module(this));
graph.inject(this);
}
}
public class bar {
#Inject Baz baz;
#Inject FooActivity activity;
public bar(FooActivity activity) {
this.activity = activity;
}
}
The problem is that 'baz' in the class 'Bar' is always null after the injection. It works though, if I don't use a provide method but only use field injection and handle all dependencies in 'Module'.
So from my point of view there are three possibilities:
1) Do field injection only and handle all dependencies in module classes.
2) Do constructor injection only and have all dependencies as constructor parameters.
3) Mix both but call .inject(this) on a ObjectGraph-object in the constructor to satisfy fields with #Inject annotation.
Is this correct?
EDIT:
I cannot use complete constructor injection because 'baz' is declared in another module. Editing the example.
#Module(includes = Module.class)
public class BasicModule {
#Provides
public baz provideBaz() {
return new baz();
}
}
I think what you are trying to do is possible. I think you want to add your activity graph to your main application graph in dagger.
Check out https://github.com/square/dagger/tree/master/examples/android-activity-graphs.
Also your class bar, you should probably do complete constructor injection. But if you MUST do it the way you are doing, you need to call inject and register bar in a module. Here is the way I would create Bar.
public class Bar {
private final Activity activity;
private final Baz baz;
#Inject public bar(FooActivity activity, Baz baz) {
this.activity = activity;
this.baz = baz;
}
}

Categories

Resources