I'm trying to create the following scenario:
public interface DaggerInjectionFactory {
void inject(MyActivity myActivity);
}
#Singleton
#Component(modules = {RestModule.class})
public interface MyRestComponent extends DaggerInjectionFactory {
RestClient provideRestClient();
}
#Singleton
#Component(modules = {PreferencesModule.class})
public interface MyPreferencesComponent extends DaggerInjectionFactory {
SharedPreferences provideSharedPreferences();
}
The dagger compiler gives me the following error as a response:
error: RestClient cannot be provided without an #Provides- or #Produces-annotated method.
The RestModule contains a #Provides #Singleton RestClient provideRestClient() method and it is also worth noting that when I remove the extends DaggerInjectionFactory from the MyPreferencesComponent the dagger compiler has no issue in generating the component builders.
What I'm trying to do is create an interface with all the injectable classes, in which I want to use the #Inject annotation, and implement them in 'all' of my components so I don't have to add the void inject(MyActivity myActivity); to all of my components.
Because I'm new to the framework I have no idea what the correct terminology is and therefore have no real clue as to what I need to search for.
So my questions are: is it possible to create such a structure, to define an interface which automatically adds all the void inject() methods to 'all' of my components? and if so, how can it be done?
-- Edit --
The expected usage would be something like:
public MyActivity extends Activity {
private RestClient restClient;
private SharedPreferences sharedPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyRestComponent.Instance.build().inject(this);
MyPreferencesComponent.Instance.build().inject(this);
}
#Inject
public void setRestClient(RestClient restClient){
this.restClient = restClient;
}
#Inject
public void setSharedPreferences(SharedPreferences sharedPreferences){
this.sharedPreferences = sharedPreferences;
}
}
You don't need to create components for each module. You can just add more modules to component and as provide all classes in on this one interface.
#Singleton
#Component(
modules = {RestModule.class, PreferencesModule.class}
)
public interface AppComponent {
void inject(MainApplication app)
void inject(MainActivity activity);
}
if you are asking for inject component to all activites/fragment/MainApplication there is no method for this. You have to specify which of these above will get dependency injection.
Related
I've just got back to the Java&Android world.
In my search for a good project starters that will leverage compilation time DI and MvvM, I found these two:
Writing Testable Android MVVM
Countries - A sample Android app
Now to my problem...
I'd like to make a base activity looks like this (keeping the important parts):
public abstract class ViewModelActivity<VM extends IViewModel> extends AppCompatActivity {
private ActivityComponent activityComponent;
#Inject
private VM viewModel;
protected void inject(AppComponent appComponent) {
appComponent.inject(this);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppComponent appComponent = ((MvvmApplication) getApplication()).getAppComponent();
inject(appComponent);
activityComponent =
DaggerActivityComponent.builder()
.appComponent(appComponent)
.activityModule(new ActivityModule(this))
.build();
ViewModel.State savedViewModelState = null;
if (savedInstanceState != null) {
savedViewModelState = savedInstanceState.getParcelable(EXTRA_VIEW_MODEL_STATE);
}
viewModel = createViewModel(savedViewModelState);
}
}
And so my AppComponent should look like:
#AppScope
#Component(modules = {
AppContextModule.class,
AppModule.class,
NetworkModule.class,
GsonModule.class
})
public interface MyAppComponent extends AppComponent {
void inject(DashboardActivity baseActivity);
Picasso picasso();
}
Where AppComponent is:
#AppScope
public interface AppComponent {
Context appContext();
void inject(ViewModelActivity viewModelActivity);
}
And finally the concrete Activity is:
public class DashboardActivity extends ViewModelActivity<DashboardViewModel> {}
Now I keep getting the following error:
Error:(29, 10) error: activities.dashboard.DashboardViewModel 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.
activities.dashboard.DashboardViewModel is injected at
mvvm.activity.ViewModelActivity.viewModel
activities.dashboard.DashboardActivity is injected at
app.inject(baseActivity)
What am I missing?
Thanks!
To use MVVM with dagger check out this sample code from google
It really did it very well and it uses the latest trends in dagger as well, like defining submodules and using new AndroidInjection for injecting activities and fragments. It also uses a custom factory for creating your view model that takes care of injecting the constructor fields.
I'm having a similar problem like the one in this question.
While the accepted answer does help, but I'm missing final piece to solve the problem.
I have 2 android library modules: common and exp which depends on common.
Everything under common:
#Module
public class CommonModule {
#Singleton
#Provides
public Repository providesRepository() {
return new Repository();
}
}
#Singleton
#Component(modules={CommonModule.class})
public interface CommonComponent {
void inject(CommonClass commonClass);
/**
CommonClass needs instance of Repository
**/
}
public class CommonDIHolder {
public static CommonComponent sComponent;
public static void init() {
sComponent = DaggerCommonComponent.builder().build();
}
}
Everything under exp:
#Module(includes={CommonModule.class})
public class ExpModule {
#Singleton
#Provides
public ExpResource provideExpResource() {
return new ExpResource();
}
}
#Singleton
#Component(modules={ExpModule.class}, dependencies={CommonComponent.class})
public interface ExpComponent {
void inject(ExpClass expClass);
/**
ExpClass needs instance of Repository and ExpResource
**/
}
public class ExpDIHolder {
public static ExpComponent sComponent;
public static void init() {
sComponent = DaggerExpComponent.builder()
.commonComponent(CommonDIHolder.sComponent)
.build();
}
}
I need both CommonClass and ExpClass receive the same instance of Repository.
The problem with this approach is that #Singleton can't depends on #Singleton. So I have to change the scope of ExpComponent into self-defined scope called #ExpScope. Then I changed the provideExpResource into #ExpScope as well.
Then I encountered an error saying that ExpComponent may not reference bindings with different scopes. It refers to the provideRepository which has different scope (#Singleton) on it. If I changed the scope into ExpScope then the CommonComponent will have different scope with provideRepository.
If I changed all #Singleton into #ExpScope then I receive this error message: depends on scoped components in a non-hierarchical scope ordering
What should I do? Or I'm doing the wrong approach here?
Use one and only one #Singleton scoped component
You should have one and only one #Singleton scoped component like this:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
}
Only specify Activities, Fragments, and Services as explicit injection targets for Components
In an Android app, you should only list Activities, Fragments and Services as injection sites. You should configure Dagger 2 to inject the rest of your dependencies without having to resort to calling component.inject(this) inside them.
For example, if your CommonClass looks like this:
public class CommonClass {
#Inject Repository repository;
public class CommonClass() {
CommonComponentHolder.get().inject(this);
}
}
Refactor it like this:
public class CommonClass {
private final Repository repository;
#Inject
public class CommonClass(Repository repository) {
this.repository = repository;
}
}
Now when you have an Activity or Fragment that needs CommonClass and you are injecting with CommonComponent or one of its sub-components or dependent components, they can obtain instances of CommonClass wired with the correct dependencies:
public class MyActivity extends AppCompatActivity {
#Inject CommonClass commonClass;
public void onCreate(Bundle savedInstanceState) {
CommonComponentHolder.getComponent().inject(this);
}
}
Use subcomponents or dependent components to specify the injection targets
Now you have a #Singleton scoped component, you'll probably want to create a component for a narrower scope for your Activity or Fragment. You'll have to connect it to your CommonComponent, so use dependent components or subcomponents (subcomponents are preferred as of Dagger 2.10). Since you say you have already tried defining a #ExpScope, I think the missing piece is to make subcomponent or dependent component with the #ExpScope that injects your Activity or Fragment.
Something like the following for the top-level singleton component:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
ExpComponent.Builder exComponent();
}
And then for the subcomponent:
#ExpScope
#Subcomponent(modules = {NarrowerScopedModule.class})
public interface ExpComponent {
#Subcomponent.Builder
public interface Builder {
Builder narrowerScopedModule(NarrowerScopedModule narrowerScopedModule);
ExpComponent build();
}
}
There are good working examples of Android projects in the Google Android Architecture Blueprints Github repo
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?
I have:
#Inject
AdalService adalService;
#Inject
Realm realm;
Both of these come from two different Components.
AdalComponent
#UserScope
#Component(dependencies = {NetComponent.class}, modules = AdalServiceModule.class)
public interface AdalServiceComponent
{
void inject(MainActivity activity);
void inject(EventsJob eventsJob);
}
RealmComponent
#UserScope
#Subcomponent(modules = RealmModule.class)
public interface RealmComponent
{
void inject(EventsJob eventsJob);
}
But I get the following error:
Error:(16, 10) error: io.realm.Realm cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
io.realm.Realm is injected at
com.bjss.bjssevents.jobs.EventsJob.realm
com.bjss.bjssevents.jobs.EventsJob is injected at
com.bjss.bjssevents.dagger.components.AdalServiceComponent.inject(eventsJob)
RealmModule
#Module
public class RealmModule
{
private static final String TAG = RealmModule.class.getSimpleName();
public RealmModule(#Singleton final Context context)
{
Log.d(TAG, "Configuring Realm");
Realm.init(context);
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build());
}
#UserScope
#Provides
public Realm providesRealm()
{
Log.d(TAG, "Providing Realm");
return Realm.getDefaultInstance();
}
}
Inside AdalServiceComponent and RealmComponent you have the same method:
void inject(EventsJob eventsJob);
That is unacceptable. The must be only one inject method for specified object (argument of inject method).
Also you can't inject things from two moduled at the same level. Both Component's are annotated with the same Scope: #UserScope. They don't know nothing about each other. If you want to define resources in AdalServiceComponent and RealmComponent make one of them parent Component and the other one Subcomponent. And the inject method should be in subcomponent.
Please read this excellent article series about advanced Dagger-2 behaviour to gain better understanding of this library.
For example, I have following interface:
public interface Repository {
Observable<Pojo> getPojos();
}
And its implementation:
public class RepositoryImpl implements Repository {
public RepositoryImpl() {
}
#Override
public Observable<Pojo> getPojos() {
return null;
}
}
Module:
#Module
class AppModule {
public AppModule() {
}
#Provides
#Singleton
Repository provideRepositoryImpl() {
return new RepositoryImpl();
}
}
And component:
#Singleton
#Component(modules = { AppModule.class })
public interface AppComponent {
void inject(MainActivity mainActivity);
}
When I trying to build project, I receive error as in question title. What problem in my code?
Read your error carefully (emphasis mine):
Dagger 2 error: “RepositoryImpl cannot be provided without an #Inject constructor or from an #Provides-annotated method”
Generally this means you've tried to #Inject RepositoryImpl, not #Inject Repository. This is especially important because your Module directly calls the RepositoryImpl constructor rather than letting Dagger create your RepositoryImpl using an #Inject-annotated constructor. (If you had, you could make RepositoryImpl a parameter of your #Provides method or switch to a #Binds method, and you have your choice between injecting the interface versus the implementation.)
The way I setup Dagger 2 is in my projects Application I add the injection component. like so.
public class NyApplication extends Application {
InjectionComponent component;
#Override
public void onCreate() {
super.onCreate();
setDagger();
}
private void setDagger() {
component = DaggerAppComponent.builder()
.appComponent(new AppModule())
.build();
component.inject(this);
}
public InjectionComponent getComponent() {
return component;
}}
and then int my activity whatever it is. I inject on it's onCreate like this.
public class MainActivity extends Activity {
#Inject
Object object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).getComponent().inject(this);
}}
I hope this helps you.