Dagger2 - Insert value in #Module without using constructor - java

Following CodingInFlow's video tutorials on Dagger, I've reached the point where I'm seeing the use of #BindsInstance inside a #Component.Builder.
Having the following classes:
Model class:
public class DieselEngine implements Engine {
private static final String TAG = "Car";
private int horsePower;
#Inject
public DieselEngine(int horsePower) {
this.horsePower = horsePower;
}
#Override
public void start() {
Log.d(TAG, "Diesel engine started. Horsepower = " + horsePower);
}
}
the Modules:
#Module
public class DieselEngineModule {
private int horsePower;
//
// public DieselEngineModule() {
//
// }
//
// #Provides
// int provideHorsePower(#Named("dieselParam") int horsePower) {
// return horsePower;
// }
public DieselEngineModule(int horsePower) {
this.horsePower = horsePower;
}
#Provides
int provideHorsePower(int horsePower) {
return horsePower;
}
// We no longer need to manually instantiate DieselEngine obj using "new DieselEngine(horsePower)".
// Because we created a "provideHorsePower()" method, and we #Inject-ed the constructor of
// DieselEngine "DieselEngine(int horsePower){..}" so that the provideHorsePower() can inject into it.
#Provides
Engine provideEngine(DieselEngine dieselEngine) {
return dieselEngine;
}
}
public abstract class PetrolEngineModule {
// #Provides
// Engine provideEngine(PetrolEngine engine) {
// return engine;
// }
#Binds
abstract Engine bindEngine(PetrolEngine engine);
// NOTE: abstract methods are never instantiated, so we can't use normal #Provides methods, only static #Provides methods
}
the Component:
#Component(modules = {
WheelsModule.class,
DieselEngineModule.class,
})
public interface CarComponent {
Car getCar();
// Dagger 2 does not inject fields automatically. It can also not inject private fields.
// If you want to use field injection you have to define a method in your #Component interface
// which takes the instance into which you would like Dagger 2 to inject an object into this field.
// ex: all fields with #Inject from MainActivity will be injected once this method is used.
void inject(MainActivity mainActivity);
#Component.Builder
interface Builder {
#BindsInstance
Builder horsePower(#Named("horse power") int horsePower);
#BindsInstance
Builder engineCapacity(#Named("engine capacity")int engineCapacity);
#BindsInstance
Builder moduleParam(#Named("dieselParam")int someNumber);
// Builder dieselEngineModule(DieselEngineModule dem);
// dagger will automaticaly implement this method, we just have to declare it, because
// we are overwriting the builder definition
CarComponent build();
}
}
My question is: is there any way to "inject" a variable inside the PetrolEngineModule, in order to store it there, without having to add a parameter to the constructor of the module (see the commented method in the component) ?
for example: int a; inside PetrolEngineModule, which would be injected by the #Named("dieselParam") integer from the component. So that it would be set only once, when the Module is created for the first time.
I've experimented with the commented method in the DieselEngineModule, where the #Named variable is directly provided from the Component, in order to be used when providing the DieselEngine. But is that okay? I could use some advice here. Is that what I want to do? Use the #Named variable from the Component/Dependency Graph instead of storing it in the Module?

Related

Dagger 2 Android how to pass value on runtime?

So i've been learning Dagger 2 Android but i am having problem. I want to pass value on runtime. I've read a lot of blogs etc. but couldn't find any solution. Every dagger source is about Dagger 2 not Dagger 2 Android. Is there anyway to do it on Dagger 2 Android too or should i simply move to Dagger 2?
This is AppComponent.java
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ActivityBuildersModule.class,
AppModule.class,
}
)
public interface AppComponent extends AndroidInjector<BaseApplication> {
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
This is ActivityBuildersModule.java
#Module
public abstract class ActivityBuildersModule {
//For injection class
#AuthScope
#ContributesAndroidInjector(
modules = {
AuthModule.class,
InjectFragmentBuildersModule.class, //Only gonna exist on AuthActivity Scope
MainViewModelsModule.class,
})
abstract AuthActivity contributeAuthActivity();
}
This is AuthModule.java
#Module
public class AuthModule {
//SubComponent
#AuthScope
#Provides
static Tiers getTiers(){
return new Tiers();
}
#AuthScope
#Provides
static Engine getEngine(){
return new Engine();
}
#AuthScope
#Provides
static Car getCar(Tiers tiers, Engine engine, #Named("String1")String string){
return new Car(tiers,engine,string);
}
#AuthScope
#Provides
static FragmentManager getFragmentManager(AuthActivity authActivity){
return authActivity.getSupportFragmentManager();
}
}
And lastly AuthActivity.java
public class AuthActivity extends DaggerAppCompatActivity {
private static final String TAG = "AuthActivity";
#Inject
Car car;
#Inject
#Named("String1")
String test1;
#Inject
#Named("String2")
String test2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth);
car.startCar();
car.setmBrand("new test brand");
NavController navController= Navigation.findNavController(this,R.id.nav_host_fragment);
navController.navigate(R.id.injectFragment);
}
}
So lets say i want to pass some string to Car instead of #Named("String1") how can i do it? I solved the problem with setters but i can't solve that problem like that everytime.

Singleton scope throwing error in dagger 2

I have a pojo, decorated with Dagger 2's #Singleton annotation
#Singleton
public class CommonDataSingleton {
private String authToken;
private boolean isAuthenticated;
}
I have to inject this as a singleton in an activity.
I have created a module to tell how the object of CommonDataSingleton should be created.
#Module
public class SingletonModule {
#Provides
CommonDataSingleton getCommonDataSingleton() {
return new CommonDataSingleton();
}
}
And a component describing the places where the object should be injected
#Component(modules = {SingletonModule.class})
public interface SingletonComponent {
void inject(LoginActivity loginActivity);
void inject(LoginPresenter loginPresenter);
}
Along with this I have another Component for injecting completely different objects.
#Component(modules = {PresenterModule.class})
public interface DiComponent {
//to update the fields in the activities
void inject(LoginActivity loginActivity);
void inject(HomeActivity homeActivity);
}
But I get this weird error stating
DiComponent (unscoped) may not reference scoped bindings:
#Singleton test.in.singleton.CommonDataSingleton
I'll provide you some sketch, haven't tested it. Let me know whether some edits must be done here, but the concept is the following:
public class CommonDataSingleton {
private String authToken;
private boolean isAuthenticated;
}
#Module
public class SingletonModule {
#Singleton
#Provides
CommonDataSingleton getCommonDataSingleton() {
return new CommonDataSingleton();
}
}
#Singleton
#Component(modules = {SingletonModule.class})
public interface SingletonComponent {
void inject(LoginActivity loginActivity);
void inject(LoginPresenter loginPresenter);
CommonDataSingleton providesCommonDataSingleton();
}
#YourCustomScopeHere
#Component(modules = {PresenterModule.class}, dependencies = {SingletonComponent.class})
public interface DiComponent {
//to update the fields in the activities
void inject(LoginActivity loginActivity);
void inject(HomeActivity homeActivity);
}

Presenter injection with Dagger 2

I just started using Dagger 2 and I found online thousands guides each one with a different implementation and I'm a bit confused now.
So basically this is what I wrote at the moment:
AppModule.java:
#Module
public class AppModule {
Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
#Provides
#Singleton
Application providesApplication() {
return mApplication;
}
}
DataModule.java:
#Module
public class DataModule {
private static final String BASE_URL = "http://beta.fridgewizard.com:9001/api/";
#Provides
#Singleton
NetworkService provideNetworkService() {
return new NetworkService(BASE_URL);
}
#Provides
#Singleton
SharedPreferences provideSharedPreferences(Application app) {
return PreferenceManager.getDefaultSharedPreferences(app);
}
}
PrefsModel.java:
#Module(includes = DataModule.class)
public class PrefsModel {
#Provides
#Singleton
QueryPreferences provideQuery(SharedPreferences prefs) {
return new QueryPreferences(prefs);
}
}
AppComponent.java (I'm exposing QueryPreferences object since I need it in a presenter, hopefully is correct in this way):
#Singleton
#Component(modules = {AppModule.class, DataModule.class, PrefsModel.class})
public interface AppComponent {
void inject(HomeFragment homeFragment);
QueryPreferences preferences();
NetworkService networkService();
}
Then I have the FwApplication.java:
public class FwApplication extends Application {
private static final String TAG = "FwApplication";
private NetworkService mNetworkService;
private AppComponent mDataComponent;
#Override
public void onCreate() {
super.onCreate();
buildComponentAndInject();
}
public static AppComponent component(Context context) {
return ((FwApplication) context.getApplicationContext()).mDataComponent;
}
public void buildComponentAndInject() {
mDataComponent = DaggerComponentInitializer.init(this);
}
public static final class DaggerComponentInitializer {
public static AppComponent init(FwApplication app) {
return DaggerAppComponent.builder()
.appModule(new AppModule(app))
.dataModule(new DataModule())
.build();
}
}
}
Finally I added another module for the presenters:
#Module
public class PresenterModule {
#Provides
Presenter<FwView> provideHomePresenter(NetworkService networkService) {
return new HomePresenterImpl(networkService);
}
#Provides
Presenter<FwView> provideSearchPresenter(NetworkService networkService) {
return new SearchPresenterImpl(networkService);
}
}
And the following component (which returns error because I cannot add a scoped dependencies here):
#Component(dependencies = AppComponent.class, modules = PresenterModule.class)
public interface PresenterComponent {
void inject(HomePresenterImpl presenter);
}
So, I have few questions that are not clear for me reading the documentation online:
How can I fix the error in the presenter component since it depends on NetworkService which is a singleton defined in the AppComponent?
I have an HomeFragment which should implement the HomePresenter with "new HomePresenter(networkService)" but now I don't know how to use the DI defined
EDIT - FIX:
HomeFragment.java:
public class HomeFragment extends Fragment {
private static final String TAG = "FW.HomeFragment";
#Inject
HomePresenterImpl mHomePresenter;
public static HomeFragment newInstance() {
return new HomeFragment();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
}
Then I modified the presenter constructor in this way:
#Inject
public HomePresenterImpl(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}
Then NetworkService is injected automatically.
I was wondering if it is correct in this way since I have to call for every fragment I have that needs a presenter constructed in the same way as the one above the following code:
FwApplication.component(getActivity()).inject(this);
You are mixing thing up. To provide your presenter, you should switch to something like the following:
Use constructor injection if possible. It will make things much easier
public class HomePresenterImpl {
#Inject
public HomePresenterImpl(NetworkService networkService) {
// ...
}
}
To provide the interface use this constructor injection and depend on the implementation:
Presenter<FwView> provideHomePresenter(HomePresenterImpl homePresenter) {
return homePresenter;
}
This way you don't have to call any constructors yourself. And to actually inject the presenter...
public class MyFragment extends Fragment {
#Inject
Presenter<FwView> mHomePresenter;
public void onCreate(Bundle xxx) {
// simplified. Add your modules / Singleton component
PresenterComponent component = DaggerPresenterComponent.create().inject(this);
}
}
This way you will inject the things. Please read this carefully and try to understand it. This will fix your major problems, you still can not provide 2 presenters of the same type from the same module (in the same scope)
// DON'T
#Provides
Presenter<FwView> provideHomePresenter(NetworkService networkService) { /**/ }
#Provides
Presenter<FwView> provideSearchPresenter(NetworkService networkService) { /**/ }
This will not work. You can not provide 2 objects of the same kind. They are indistinguishable. Have a look at #Qualifiers like #Named if you are sure this is the way you want to go.
You do not have to provide Presenter if #Inject annotation is used in the constructor. #Inject annotation used in the constructor of the class makes that class a part of dependencies graph. So, it also can be injected when needed.
On the other hand, if you add #Inject annotation to fields, but not to constructors, you have to provide that class.

Resolve named dependency with Dagger 2 after the class is initialized

I am using Dagger 2 for the dependency management of my Java application.
I have the following structure:
public interface SecondaryService
{
void doSomethingElse(String data);
}
public class SecondaryServiceFirstImpl implements SecondaryService
{
public void doSomethingElse(String data)
{
// Do something else
}
}
public class SecondaryServiceSecondImpl implements SecondaryService
{
public void doSomethingElse(String data)
{
// Do something else
}
}
public interface MainInterface
{
void doSomething(String data);
}
public class MainService implements MainInterface
{
private SecondaryService secondaryService;
private DatabaseService databaseService;
public MainService(SecondaryService secondaryService, DatabaseService databaseService)
{
this.secondaryService = secondaryService;
this.databaseService = databaseService;
}
public void doSomething(String data)
{
String name = databaseService.getName(data);
// Resolve the NAMED SecondaryService based on the name property and
// use the implementation.
}
}
And here is the Dagger Module code:
#Module
public class DependencyRegisterModule
{
#Provides #Named('first')
SecondaryService provideSecondaryServiceFirstImpl ()
{
return new SecondaryServiceFirstImpl ();
}
#Provides #Named('second')
SecondaryService provideSecondaryServiceSecondImpl ()
{
return new SecondaryServiceSecondImpl ();
}
#Provides
DatabaseService provideDatabaseService ()
{
return new DatabaseServiceImpl();
}
#Provides
MainInterface provideMainInterface(SecondaryService secondaryService, DatabaseService databaseService)
{
return new MainService (secondaryService, );
}
}
As you can see I have a SecondaryService interface that is implemented by two classes. I want to resolve the named dependency for the SecondaryService based on a parameter, that I get from the database inside a method in the MainService.
Is there a way to do this? If this does not work with a Named dependencies, is there another way to do this?
So far I have used a factory pattern, but it is very hard to manage, as I have to pass the dependencies of the classes inside their constructor.
#Named—or #Qualifiers in general—are intended to be used if you need 2 objects of the same kind, e.g. injecting a String username and String email. In this case a qualifier could be used to distinguish between those objects.
In your case it seems that you want either one object or the other. In your case you should use different modules if you want to take a clean approach, or add an if/else to your module in the constructor.
Use a base class
#Module
public abstract class ServiceModule {
#Provides
public abstract SecondaryService providesServcie();
}
Then just create 2 sub classes of this module and provide the needed service respectively.
When creating your component just supply the right implementation of your module
if(1 == 1)
new MyComponent.Builder().add(new OneServiceModule()).build();
else
new MyComponent.Builder().add(new OtherServiceModule()).build();
And remember, you don't have to duplicate your other #Provides methods, since you can just use multiple modules with the component.
Use an if/else construct
#Module
public class DependencyRegisterModule
{
private int whichModule;
public DependencyRegisterModule(int whichModule) {
this.whichModule = whichModule;
}
#Provides
SecondaryService provideSecondaryServiceSecondImpl ()
{
return whichModule == 1 ? new SecondaryServiceSecondImpl() : new SecondaryServiceFirstImpl();
}
}
I hope I don't have to explain how if/else works ;)

Why can't Guice bind for an intermediate dependency?

Here's my code:
// Groovy
interface MyMapper {
Buzz toBuzz(Fizz fizz);
}
class MyMapperImpl implements MyMapper {
#Named("SIMPLE_FOOBAR")
Foobar foobar;
MyMapperImpl(Foobar foobar) {
super();
this.foobar = foobar;
}
#Override
Buzz toBuzz(Fizz fizz) {
// ...etc.
}
}
class Whistlefeather {
MyMapper mapper;
Whistlefeather(MyMapper mapper) {
super();
this.mapper = mapper;
}
void doSomething(Fink fink) {
Fizz fizz = getSomehow(fink);
Buzz buzz = mapper.toBuzz(fizz);
// Do something with 'buzz'...
}
}
class ApplicationMain {
Whistlefeather whistlefeather;
#Inject
ApplicationMain(Whistlefeather whistlefeather) {
super();
this.whistlefeather = whistlefeather;
}
static void main(String[] args) {
Injector injector = Guice.createInjector(new ApplicationModule());
ApplicationMain appMain = injector.getInstance(ApplicationMain);
appMain.run();
}
void run() {
whistlefeather.doSomething(new Fink());
}
}
Here's my Guice module:
class ApplicationModule extends AbstractModule {
#Override
protected void configure() {
// I have to name the Foobars because in reality there will be
// *many* of them, each configured slightly different.
bind(Foobar.class).annotatedWith(Names.named("SIMPLE_FOOBAR"))
.toInstance(new Foobar(true, true, false, 103, "yee haw"));
bind(MyMapper.class).to(MyMapperImpl);
}
}
Here's my exception:
Could not find a suitable constructor in com.me.myapp.MyMapperImpl.
Classes must have either one (and only one) constructor annotated
with #Inject or a zero-argument constructor that is not private.
My understanding was that I only need to annotate constructors with #Inject if I would be directly calling them through the Injector#getInstance(...) method. Since I do this with ApplicationMain, which contains a reference to Whistlefeather, which contains a reference to MyMapper, I didn't think I would have to annotate the MyMapperImpl constructor.
Any ideas as to where I'm going awry here?
In order for Guice to create any object, it has to know which constructor to use. This is true all the way down the Object Graph.
Consider the following code:
public interface Something { }
public class SomethingImpl implements Something {
private final String data;
public SomethingImpl(String data) {
this.data = data;
}
public SomethingImpl(Integer data) {
this.data = data.toString();
}
}
public class AnotherClass {
private final Something something;
#Inject
public AnotherClass(Something something) {
this.something = something;
}
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(Something.class).to(SomethingImpl.class);
bind(String.class).toInstance("Hello!");
bind(Integer.class).toInstance(50);
}
}
In this scenario, how is Guice supposed to know which constructor to use in SomethingImpl? If you were the author of Guice, how would you write it?
Obviously, you can't answer, because it's impossible. There has to be some sort of mechanism to tell Guice which constructor to use, regardless of whether or not it's called by Injector.getInstance() or not; that's why you have to annotate at least one constructor. Guice will use a no-argument constructor by default if one is specified, but if there isn't one, Guice doesn't know what to do.

Categories