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.
Related
I can't understand what wrong I am doing here
Splash Activity :
public class SplashActivity extends BaseActivity implements SplashView {
#Inject
SplashPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_with_di);
presenter.getAppVersion();
}
}
Component :
#Component(modules = SplashModule.class)
public interface AppComponent {
SplashWithDiPresenter getSplashWithDiPresenter();
}
Splash Module :
#Module(includes = RetrofitModule.class)
public class SplashModule {
#Provides
SplashPresenter provideSplashPresenter(final SplashInteractorImpl interactor){
return new SplashPresenterImpl(interactor);
}
#Provides
SplashInteractor providesSplashInteractor(final ApiInterface apiInterface){
return new SplashWithDiInteractorImpl(apiInterface);
}
}
inside application class called this method in onCreate()
private void createComponent() {
appComponent = DaggerAppComponent.builder()
.splashModule(new SplashModule())
.build();
}
Getting null object reference on Splash activity on create method
-> presenter.getAppVersion();
You have injected your application dependencies and now you need to do the same with SplashActivity dependencies. So you need to create a new component for your activity, lets say SplashComponent, and add inject method to it like this:
#PerActivity
#Component(modules = SplashModule.class, dependencies = AppComponent.class)
public interface SplashComponent {
public void inject(SplashActivity activity);
}
And then in your SplashActivity in the onCreate method add injection like this:
public class SplashActivity extends BaseActivity implements SplashView {
#Inject
SplashPresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_with_di);
DaggerSplashComponent.builder()
.appComponent(getAppication().getAppComponent())
.splashModule(new SplashModule())
.build()
.inject(this);
presenter.getAppVersion();
}
}
Note, that you need to call your presenter's method only after you have injected your dependencies with inject method!
Ideally you should look at using subcomponents for these use cases for injecting Android Components(Activities, Fragments, Services...). It's pretty simple and avoids coupling Injector Components with Android components enabling testing of these classes and switching of components for Espresso tests.
Add dagger-android deps to your project (com.google.dagger:dagger-android-support:2.x, com.google.dagger:dagger-android-processor:2.x)
And then make an abstract DepsModule module, for example -
#Module
public abstract class DepsModule {
#PerActivity
#ContributesAndroidInjector(modules={SplashModule.class})
SplashActivity provideSplashInjector()
}
And change you App Component to include AndroidSupportInjectionModule.class & DepsModule.class and you should remove SplashModule.classs from the list to avoid rebinding errors.
Now its as easy as having a DispatchingAndroidInjector<Activity> instance injected to you App class and implementing HasActivityInjector interface to return the dispatching injector.
Now in SplashActivity before invoking super.onCreate() call AndroidSupportInjection.inject(this)
#ContribbutesAndroidInjector automatically tells dagger to create a sub-component for SplashActivity and bind the injector factory with DispatchingAndroidInjector<Activity> removing the need for boilerplate sub-component classes
And other sub-component installations for different activities can be added to DepsModule to enable injecting them in a similar way.
This also helps in scope segregation as exposing a splash activity's bindings to the entire app component is not really good
Hope I could help. You can visit the dagger 2 android docs for more info on using Dagger 2
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 two components, one for Application Context and one for Activity Context as shown below.
#Singleton
#Component(modules = {AppModule.class,})
public interface AppComponent {
#ForApplication
Context context();
//Preferences prefs(); //(Question is related to this line)
}
#PerActivity
#Component(dependencies = AppComponent.class,modules = {ActivityModule.class})
public interface ActivityComponent extends AppComponent {
void inject(ActivityA activity);
}
I have a Singleton class that I want to inject in ActivityA that is declared in ActivityComponent.
#Singleton
public class Preferences {
#Inject
public Preferences(#ForApplication Context context) {
...
}
}
public class ActivityA extends AppCompatActivity {
#Inject
Preferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerActivityComponent.builder()
.appComponent(((MyApplication) getApplication()).getComponent())
.activityModule(new ActivityModule(this))
.build();
}
I am injecting AppComponent in my Applicaiton onCreate() and ActivityComponent in onCreate of Activity, in this case ActivityA like above
Problem(or Question): Currently, If I do not expose this singleton class from my AppComponent (The commented line in first code block) and do not have provides method in AppModule. I cannot compile. Compilation Error shows I cannot have a reference to different scope from ActivityComponent, which I kind of understand. That means, I cannot access Singleton scoped class with PerActivity scoped component.
But, do I have to have provides method for all Singleton class and expose it via AppComponent (which I am currently doing and works)? Is there a better way to do the Singleton class injection?
There is another way. Declare Activity component as Application subcomponent. This way everything declared in Application component is visible in Activity subcomponent.
Access to the Activity subcomponent can be done through Acpplication component:
GithubClientApplication.get(this)
.getAppComponent()
.provide(new ActivityModule(this))
Have a look at this excellent article about Dagger components, especially in Implementation section.
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.