I am using Dagger 2 injection to provide some dependency to client:
public class Dependency {
#Inject
Dependency() {
}
void check() {
System.out.print("Instantiated");
}
}
public class Client {
#Inject Dependency dependency;
Client() {
ClientComponent component = DaggerClientComponent.create();
component.inject(this);
}
void checkDependency() {
dependency.check();
}
}
#Component
public interface ClientComponent {
void inject(Client client);
}
public class Test {
public static void main(String... args) {
Client client = new Client();
client.checkDependency();
}
}
It works fine, but now I want to make my dependency singleton. How can I achieve it? Should I create module for this dependency and annotate provide method with singleton annotation or I have another options to avoid module creation?
Add #Singleton on the top of your class and add #Singleton annotation to your component.
#Singleton
public class Dependency {
#Inject
Dependency() {
}
void check() {
System.out.print("Instantiated");
}
}
#Singleton
#Component
public interface ClientComponent {
void inject(Client client);
}
You should also move creation of your component to better place - onCreate method from app object is right place.
Related
I'm just getting started with Dagger & Dependency Injection and wondering about configuration at runtime for some of the lower-level dependencies. Is there a way to provide a low-level injected Singleton with a configuration object at runtime?
Basic idea of what I'm after:
#Singleton
class DatabaseService {
#Inject
public DatabaseService(DatabaseConnectionConfig config) { // how can this arg be passed in at runtime?
// make the connection
}
}
#Singleton
class HighLevelService {
#Inject
public HighLevelService(DatabaseService db) {
}
}
#Module
class Module {
#Binds
abstract HighLevelService bindHighLevelService(HighLevelService svc);
#Binds
abstract DatabaseService bindDatabaseService(DatabaseService svc);
}
#Singleton
#Component(modules = {
Module.class
})
interface Factory {
HighLevelService highLevelService();
static Factory create() {
return DaggerFactory.create();
}
}
public class App {
public static void main(String[] args) {
// get the config details from the arguments
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
// is there a way to configure the DatabaseConnectionConfig from here?
HighLevelService svc = Factory.create().highLevelService();
}
}
You can use a #Component.Factory (or #Component.Builder) with #BindsInstance.
#Singleton
#Component
interface Factory {
HighLevelService highLevelService();
// This nested interface is typically called "Factory", but I
// don't want to look up how to access Factory from Factory.Factory
#Component.Factory
interface MyFactory {
Factory create(#BindsInstance DatabaseConnectionConfig config);
}
static Factory create(DatabaseConnectionConfig config) {
return DaggerFactory.factory().create(config);
}
}
public class App {
public static void main(String[] args) {
DatabaseConnectionConfig config = parseDBConfigFromArgs(args);
HighLevelService svc = Factory.create(config).highLevelService();
}
}
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);
}
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.
For example I have a class that gets a dependecy in constructor, like
class ExampleService() {
private Dependency dep;
public ExampleService(Dependency dep) {
this.dep = dep;
}
}
and Dependecy class:
class Dependency {
public static Dependency getInstance() {
return new Dependency();
}
private Dependency() {
/*constructor implementation here*/
}
}
I want to inject result of Dependency.getInstance() method into ExampleService constructor by #Inject EJB annotation. Is it possible? How? Thankyou.
In CDI a producer method can be static, so using your example, the following would work just fine:
class ExampleService() {
private Dependency dep;
#Inject
public ExampleService(Dependency dep) {
this.dep = dep;
}
}
class Dependency {
#Produces
public static Dependency getInstance() {
return new Dependency();
}
private Dependency() {
/*constructor implementation here*/
}
}
However, like mentioned in the comments to your question, there might be better ways depending on what you want.
I'm looking for a way to force certain Guice bindings to be injected as providers only. For example, when there is a configuration like
interface ResultLogger {
void log(String resultAsString);
}
class ResultLoggerProvider implements Provider<ResultLogger> {
// ...
}
class ResultDisplayModule extends AbstractModule {
#Override
protected void configure() {
bind(ResultLogger.class).toProvider(ResultLoggerProvider.class);
}
}
I would like to have way to configure my module so that a class like
#Singleton
class ResultParser {
private final Provider<ResultLogger> loggerProvider;
#Inject
public ResultParser(Provider<ResultLogger> loggerProvider) {
this.loggerProvider = loggerProvider;
}
}
can be injected just fine, but an implementation like
#Singleton
class ResultParser {
private final ResultLogger resultLogger;
#Inject
public ResultParser(ResultLogger resultLogger) {
this.resultLogger = resultLogger;
}
}
should throw a RuntimeException which notifies the developer that ResultLogger is only available via a provider. The exception would ideally be thrown as soon as possible, e.g. during construction of the injector. I'm looking for an easy way to achieve this using the existing API in Guice 3.0.
Maybe you should not implement Provider at all and just have a
#Singleton
public class ResultLoggerProvider {
public ResultLogger get() {...}
// ...
}
#Singleton
class ResultParser {
private final ResultLoggerProvider loggerProvider;
#Inject
public ResultParser(ResultLoggerProvider loggerProvider) {
this.loggerProvider = loggerProvider;
}
}
and remove the other bindings.
I think that it isn't right way. I guess you need smt like
interface ResultLogger {
void log(String resultAsString);
}
class ResultLoggerWrapper implements ResultLogger {
#Inject #Named("day") ResultLogger dayLogger;
#Inject #Named("night") ResultLogger nightLogger;
public void log(String resultAsString){
if(isDay()) {
dayLogger.log(resultAsString)
} else {
nightLogger.log(resultAsString)
}
}
}
bind(ResultLogger.class).to(ResultLoggerWrapper.class);
It should work to bind Provider instead of ResultLogger. That is in your module
bind(new TypeLiteral<Provider<ResultLogger>>(){}).to(ResultLoggerProvider.class);