I've started to setup Dagger 2 and faced a strange issue that looks like a bug to me.
I have 1 main component and 2 subcomponents which I 'plus' in parent component. I use different scopes for each subcomponent. The problem is that I can easily do fields injection for the 1st subcomponent but I can't do the same for the second. The injected fields stay nulls.
Main component:
#Singleton
#Component(modules = { WalletSaverAppModule.class })
public interface MyAppComponent {
TrackingComponent plus(TrackingModule module);
DashboardComponent plus(DashboardModule module);
}
1st subcomponent (works well):
#PerActivity #Subcomponent(modules = { DashboardModule.class })
public interface DashboardComponent {
void inject(MainActivity activity);
}
2nd subcomponent (fields injection -> null):
#PerService #Subcomponent(modules = { TrackingModule.class })
public interface TrackingComponent {
void inject(IntentService context);
}
How I do fields injection for the 2nd subcomponent:
public class TrackingService extends IntentService {
#Inject CallCase mCallCase;
#Inject CallModelMapper mCallModelMapper;
...
#Override protected void onHandleIntent(Intent intent) {
((MyApp) getApplication()).getAppComponent().plus(new TrackingModule(this)).inject(this);
// ---> here the both fields are null
...
Objects that I'm injecting:
#Singleton public class CallCase {
private CallRepository mCallRepository;
#Inject public CallCase(final CallRepository userRepository) {
mCallRepository = userRepository;
}
public Observable<Call> execute() {
...
}
}
#Singleton public class CallModelMapper {
#Inject CallModelMapper() {
}
public CallModel transform(#NonNull final Call callEntity) {
...
}
}
Both objects have #Singleton scope (as their constructor fields). Could it be a scope conflict?
--- UPDATE ---
I've checked the class generated by Dagger2 (DaggerMyAppComponent) that I'm using in MyApp to build application component. I found the difference between implementations of 1st and 2nd components.
1st:
private final class DashboardComponentImpl implements DashboardComponent {
private final DashboardModule dashboardModule;
private Provider<DashboardMvp.Presenter> providesPresenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DashboardComponentImpl(DashboardModule dashboardModule) {
this.dashboardModule = Preconditions.checkNotNull(dashboardModule);
initialize();
}
private void initialize() {...}
#Override
public void inject(MainActivity activity) {...}
}
2nd:
private final class TrackingComponentImpl implements TrackingComponent {
private final TrackingModule trackingModule;
private TrackingComponentImpl(TrackingModule trackingModule) {
this.trackingModule = Preconditions.checkNotNull(trackingModule);
// ---> look, missing call initialize() <---
}
#Override
public void inject(IntentService context) {...}
}
Why Dagger 2 takes different the subcomponents that implemented in the same way? Only one difference I can see is the scope. I would appreciate any inputs about this problem.
Thanks in advance!
In TrackingComponent why are you inject to IntentService. Maybe changing to TrackingService will help
void inject(TrackingService context);
Related
I'm trying to inject an object to my own class (OpenProjectItemHandler) as below context. But the injected object (eventBroker) is null. How can we inject the object?
public class ProjectExplorerPart {
protected TreeViewer viewer;
//#Inject IEventBroker eventBroker;
#PostConstruct
public void createComposite(Composite parent) {
//...
viewer.addDoubleClickListener(new OpenProjectItemHandler());
//...
}
}
public class OpenProjectItemHandler implements IDoubleClickListener {
#Inject IEventBroker eventBroker;
#Override
public void doubleClick(DoubleClickEvent event) {
//...
//IEclipseContext eclipseContext = E4Workbench.getServiceContext();
//eventBroker = eclipseContext.get(IEventBroker.class);
eventBroker.send("ta/project_explorer/open_item", Collections.EMPTY_LIST);
}
}
}
Objects created using new are not injected. You need to use ContextInjectionFactory.make to create the object.
public class ProjectExplorerPart {
protected TreeViewer viewer;
#PostConstruct
public void createComposite(Composite parent, IEclipseContext context) {
//...
OpenProjectItemHandler handler
= ContextInjectionFactory.make(OpenProjectItemHandler.class, context);
viewer.addDoubleClickListener(handler);
//...
}
}
Another option is to annotate the class you want to inject with the #Creatable annotation like this:
#Creatable
#Singleton
public class OpenProjectItemHandler implements IDoubleClickListener {
...
}
And then use standard injection in object managed by the framework to inject your instance
public class ProjectExplorerPart {
#Inject private OpenProjectItemHandler opih;
#PostConstruct
public void createComposite(Composite parent) {
//...
viewer.addDoubleClickListener(opih);
//...
}
}
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 had this class as follows which works fine
#Singleton
public class EmpResource {
private EmpService empService;
#Inject
public EmpResource(EmpService empService) {
this.empService=empService;
}
}
public class EmpService {
public void getName(){..}
}
Now instead of using EmpService directly, I had to create an interface and EmpService implement that interface as follows.
public interface IEmpService{
void getName();
}
public class EmpServiceImpl implements IEmpService {
public void getName(){...}
}
So now my resource class has to use the interface but I am not sure how to reference the implementation it has to use.
#Singleton
public class EmpResource {
private IEmpService empService;
#Inject
public EmpResource(IEmpService empService) {
this.empService=empService;
}
}
I've seen this and I wasn't sure where my binding should go. (This is my first project related to Guice so I am a total newbie).
This is the error that came "No implementation for com.api.EmpService was bound." which is totally understandable but not sure how to fix it.
I appericiate your help.
FYI: I am using Dropwizard application.
You would configure your module similar to this:
public class YourModule extends AbstractModule {
#Override
protected void configure() {
bind(EmpService.class).to(EmpServiceImpl.class);
// ....
}
}
you also have to add a Provide Methode for your EmpServiceImpl class
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(IEmpService.class).to(EmpServiceImpl.class);
}
#Provides
EmpServiceImpl provideEmpServiceImpl() {
// create your Implementation here ... eg.
return new EmpServiceImpl();
}
}
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.
i'm trying to use Dagger 2 into my apps but i'm having some problems regarding the entities repository and i haven't figured it out what i'm missing.
Here is my Application Component:
#Singleton
#Component(
modules = {
AndroidModule.class,
RepositoryModule.class
}
)
public interface ApplicationComponent {
void inject(AndroidApplication app);
IDependenceyRepository dependencyRepository();
}
My modules:
#Module
public class RepositoryModule {
#Provides #Singleton IDependenceyRepository provideDependendencyRepository(Context context) {
return new DependencyRepository(context);
}
}
#Module
public class AndroidModule {
private final AndroidApplication app;
public AndroidModule(AndroidApplication app) {
this.app = app;
}
#Provides #Singleton Context provideApplicationContext() {
return app;
}
}
My AndroidApplication:
public class AndroidApplication extends Application {
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
setupGraph();
}
private void setupGraph() {
component = DaggerApplicationComponent.builder()
.androidModule(new AndroidModule(this))
.repositoryModule(new RepositoryModule())
.build();
component.inject(this);
}
public ApplicationComponent component() {
return component;
}
}
And here is my activity:
public class MainActivity extends Activity {
#Inject
IDependenceyRepository repository;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.testRepository();
}
private void testRepository() {
Dependency dep = new Dependency();
dep.setDescription("XXX");
dep.setName("AAAA");
try {
repository.save(dep);
Log.d("", repository.queryAll().get(0).getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
What happens is that i'm getting a null pointer exception into the repository. He is not being injected.
If i add this line though:
repository = ((AndroidApplication) getApplication()).component().dependencyRepository();
It works, but the point of the DI it's not to have to worry about this, or am im wrong about that?
I've tried some other example and tutorials but haven't managed to solve my problem.
Thanks in advance,
david.mihola's comment is correct: in order to be able to have #Inject fields initialized, you need to have a method (typically void inject(MyClass)) in your component.
It's worth noting (not specifically for your question, but it could come up) that if you have a base class:
public class BaseActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((AndroidApplication) getApplication()).component().inject(this);
}
}
and in your component:
void inject(BaseActivity);
Injecting into a subclass that has it's own #Inject fields won't work:
public class ActualActivity extends BaseActivity {
#Inject IDependenceyRepository repo;
}
This is because Dagger needs to know the concrete classes that will be injected at compile time, not runtime (like Dagger 1 could do).