I'm trying to setup an MVP application, I want to Inject my interactor into Presenter Class instead of using the new keyword.
See Example below:
// Example presenter Implementation
public class ExamplePresenterImpl implements ExamplePresenter{
private final Application application;
private ExampleView exampleView;
private ExampleInteractorImpl interactor;
public ExamplePresenterImpl(Application application){
this.application = application;
// I WANT TO GET RID OF THIS AND INJECT INSTEAD.
interactor = new ExampleInteractorImpl(application);
}
#Override
public void setView(ExampleView exampleView){
this.exampleView = exampleView;
}
public void callInteractorMethod(){
// call Fetch method from Interactor
interactor.fetchData();
}
}
// Interactor
public class ExampleInteractorImpl implements ExampleInteractor {
private final Application application;
public ExamplePresenterImpl(Application application){
this.application = application;
}
public List<String> fetchData(){
// return value to the called function
}
}
You could pass the interactor into the constructor of the presenter :
public class MyPresenterImpl implements MyPresenter {
private MyView view;
private MyInteractor interactor;
public MyPresenterImpl(MyView view, MyInteractor interactor) {
this.view = view;
this.interactor = interactor;
}
}
Then in your module :
#Singleton #Provides
public MyInteractor provideMyInteractor(Dependencies...){
return new MyInteractorImpl(your_dependencies);
}
#Singleton #Provides
public MyPresenter provideMyPresenter(MyView view, MyInteractor interactor){
return new MyPresenterImpl(view, interactor);
}
Or you could annotate both Presenter and Interactor constructors with #Inject annotation.
I made an example with a simple login page, you can take a look at it if you need :
https://github.com/omaflak/Dagger2-MVP
You should inject presenter into View(e.g. into Activity) class. Create a module like ExampleModule and a component like ExampleComponent, which will provide the presenter. Presenter should have a constructor that expects all needed dependencies. In this example, dependencies are Application and the interactor
Related
I create android ViewModel in Activity:
EventViewModel model = ViewModelProviders.of(this).get(EventViewModel.class);
For this I create EventViewModel :
public class EventViewModel extends ViewModel {
private final EventDao eventDao;
public EventViewModel(EventDao eventDao) {
this.eventDao = eventDao;
}
public void createEvent(final Event event) {
new Thread(new Runnable() {
#Override
public void run() {
eventDao.insert(event);
}
}).start();
}
}
I learned that I must create custom factory in order to inject my EventDao to EventViewModel. Ok, let's say i did it.
public class ViewModelFactory implements ViewModelProvider.Factory {
private final EventDao eventDao;
#Inject
public ViewModelFactory(EventDao eventDao) {
this.eventDao = eventDao;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass == EventViewModel.class) {
return (T) new EventViewModel(eventDao);
}
return null;
}
}
but what to do next? I see several ways. If belive to examples from google I must do next in MyActivity:
EventViewModel model = ViewModelProviders.of(this, new ModelFactory(eventDao)).get(EventViewModel.class);
1) But where do i get eventDao in MyActivity?
2) Do I need create custom ModelFactory for each ViewModel if it use dao class in?
I use Dagger 2 and I just want understand how can I create ViewModel with DAO and use this ViewModel in MyActivity?
You inject ViewModelFactory in the activity. ViewModelFactory will get EventDao through constructor injection. You pass the injected ViewModelFactory instance say viewModelFactory to ViewModelProviders
EventViewModel model = ViewModelProviders.of(this, viewModelFactory).get(EventViewModel.class);
I have a module as follows.
#Module
public class AppModule {
private final Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Architecture.ApplicationContext
Context provideContext() {
return app;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return provideContext();
}
#Singleton
#Provides
Application provideApp() {
return app;
}
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context context, SoundsDAO soundsDAO) {
return new SoundsRepository(context, soundsDAO);
}
}
A component like this.
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
void inject(PostView postView);
void inject(MediaPlayerService mediaPlayerService);
}
In activity, fragment or service, I do this
#Inject
SoundsRepository soundsRepository;
#Override
protected void onCreate(...) {
//....
((Global) getApplication()).getComponent().inject(this);
}
In SoundsRepository
#Singleton
public class SoundsRepository {
#Inject
public SoundsRepository(Context context, SoundsDAO soundsDAO) {
this.context = context;
this.soundsDAO = soundsDAO;
System.out.println(TAG + "INIT");
}
// ....
}
So, now, every time I start to access an activity or service where SoundsRepository is injected, I get a new instance, I mean, the constructor of "SoundsRepository" fires again.
What am I doing wrong?
EDIT : Inject in Application Class
public class Global extends MultiDexApplication {
protected AppComponent appComponent;
private boolean calledAlready = false;
#Override
public void onCreate() {
super.onCreate();
//if (LeakCanary.isInAnalyzerProcess(this)) return;
//LeakCanary.install(this);
initFirebasePersistance();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
appComponent.inject(this);
FrescoUtil.init(getApplicationContext());
}
public AppComponent getComponent() {
return appComponent;
}
}
In your module you have a method that provides an instance of SoundsRepository - good
In your AppComponent you are missing a:
SoundsRepository soundsRepository();
In your Global which extends Application/MultidexApplication you create your DaggerAppComponent - good
In your other activities/fragments/services just call:
Global application = (Global) getApplication();
SoundsRepository sr = application.getComponent().soundsRepository()
Android guarantees you have only one instance of your Application (Global) class for all other actvities/services (its somewhat like a singleton).
So keep your component in that application class, and whenever you need your class, call: (YourApplication) getApplication().getComponent().yourSingleInstanceSomething();
I created and tested sample code for you: https://github.com/zakrzak/StackDaggerTest
Dagger's #Singleton is just a scope, and does not guarantee returning a singular instance of a class.
In my understanding, if you:
void inject(PostView postView);
you tell Dagger to make everything you annotated with #Provided in AppModule accessible in your PostView as soon as you request it with:
#Inject
SoundsRepository soundsRepository;
then dagger just calls the #provided method which in your case returns a new SoundRepository instance:
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context ........) {
return new SoundsRepository(...);
}
which causes your problem
after search on web for learning about this feature most topics or post was using dependency injection for Retrofit or other android useful libraries, but i have some custom class which i want to use that with DI and i can't done it, for example i have simple custom class for using SharePreference and i'm using with that as an Singleton class
in my code i can't assign correct Dagger to component on SpApplication class to use that on activities or fragments
public class SP {
private SharedPreferences preferences;
private Context context;
public SP(Context context) {
this.context = context;
}
private SharedPreferences getPrefs() {
return preferences = PreferenceManager.getDefaultSharedPreferences(context);
}
public String getString(SharedPrefsTypes propertyName) {
return getPrefs().getString(propertyName.toString(), "");
}
public int getInt(SharedPrefsTypes propertyName) {
return getPrefs().getInt(propertyName.toString(), 0);
}
...
public enum SharedPrefsTypes {
Login
}
}
now i'm trying to use DI for that:
AppModules class:
#Module
public class AppModules {
private Context context;
public AppModules(Context context) {
this.context = context;
}
#Provides
#Singleton
SP provideSharePreferences() {
SP sharePreference = new SP(context);
return sharePreference;
}
}
ApplicationComponent class:
#Component(modules = AppModules.class)
public interface ApplicationComponent {
void inject(ActivityMain activity);
}
SpApplication class:
public class SpApplication extends Application {
private static SpApplication self;
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
self = this;
component = DaggerApplicationComponent.builder().build();
}
public static SpApplication get(Context context) {
return (SpApplication) context.getApplicationContext();
}
public static SpApplication getInstance() {
return self;
}
public ApplicationComponent getComponent() {
return component;
}
}
and my ActivityMain class:
public class ActivityMain extends AppCompatActivity {
#Inject
SP sharePreference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((SpApplication) getApplication()).getComponent().inject(this);
sharePreference.setInt(SP.SharedPrefsTypes.Login, 0);
}
}
I get this error:
android.app.Application cannot be cast to com.pishguy.yendir.SpApplication
Thanks in advance
I guess you are trying to inject into ActivityMain, but since you did not provide its source, let me show you how you could inject into SpApplication. Then just copy the relevant parts into your Activity.
Few things I think you need to change.
Module:
Your AppModules class is generally OK, but I just suggest you to change the way you make use of Context - don't use it as a field, but inject it as any other service. It looks like this:
#Module
public class AppModules {
private Context context;
public AppModules(Context context) {
this.context = context;
}
#Provides // this can be non-scoped because anyway the same instance is always returned
Context provideContext() {
return this.context;
}
#Provides
#Singleton
SP provideSharePreferences(Context context) {
return new SP(context); // use method-local Context
}
}
Component:
If component injects scoped services (#Singleton is a scope), the component itself must be scoped
If you want to inject into SpApplication class, then declare it as an injection client in the component
Taking into account these two points, ApplicationComponent should look like this:
#Singleton // injects #Singleton scoped services
#Component(modules = AppModules.class)
public interface ApplicationComponent {
void inject(SpApplication application); // SpApplication is DI client (injection target)
}
Client:
I'd change few things in your SpApplication class:
The way you instantiate ApplicationComponent is incorrect
This is not a bug, but you don't really need get(Context) and getInstance() methods
In addition, since I'm showing how you inject into SpApplication, I will add the injection logic as well (which you should copy to your actual clients).
So, SpApplication (which is DI client) should look similar to this:
public class SpApplication extends Application {
#Inject SP sp; // the dependency that should be injected
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
getComponent().inject(this); // this is when the actual injection takes place
}
public ApplicationComponent getComponent() {
if (component == null) {
// this is the way Dagger components should be instantiated
component = DaggerApplicationComponent.builder()
.appModules(new AppModules(this))
.build();
}
return component;
}
}
If you perform the above changes, I tend to believe that you'll be all right.
BTW, I recently completed a blog post about Dagger 2 scopes. You might want to check it if you are going to be serious about dependency injection.
I'm trying to understand and implement Dagger 2. I've already read a lot of different tutorials and official documentation. I think I understand it in general but I still can not understand some simple points (while I wrote it I've found solution for some but..):
It's possible for #Provides methods to have dependencies of their own.
When it's possible?
What I see it's possible to get "component contains a dependency cycle".
Can someone help me to understand cases when it possible and when it not possible.
Thanks.
Actually, you can use qualifiers (#Named("something") annotation) to get multiple different type of implementation for a given dependency.
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
#Named("first")
BaseNavigator firstNavigator();
#Named("second")
BaseNavigator secondNavigator();
Context context();
//...
}
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
#Named("first")
BaseNavigator provideFirstNavigator() {
return new SomeNavigator();
}
#Provides
#Singleton
#Named("second")
BaseNavigator provideSecondNavigator() {
return new OtherNavigator();
}
#Provides
Context provideApplicationContext() {
return this.application;
}
}
public abstract class BaseActivity extends Activity {
#Inject
#Named("second")
BaseNavigator navigator;
After very long way of many experiments I've found the answer.
I'm making this answer as cue card for myself and hope it can help other daggers jedis.
So we have dagger structure
AndroidApplication
BaseActivity
Navigator
ApplicationComponent
ApplicationModule
...
ApplicationComponent.class
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
Navigator navigator();
Context context();
//...
}
ApplicationModule.class
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
Navigator provideNavigator() {
return new Navigator();
}
#Provides
#Singleton
Context provideApplicationContext() {
return this.application;
}
}
Navigator.class
#Singleton
public class Navigator implements BaseNavigator {
public Navigator() {}
}
BaseActivity.class
public abstract class BaseActivity extends Activity {
#Inject
Navigator navigator;
//code here
}
This code will work and BaseActivity will get navigator as new Navigator() provided by ApplicationModule.
But if you have several implementation of BaseNavigator class you can get some certain implementation for example Navigator class without creating new instance manually.
*This construction will give you "component contains a dependency cycle"
#Provides
#Singleton
Navigator provideNavigator(Navigator navigator) {
return navigator;
}
You can do this:
ApplicationComponent.class
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
BaseNavigator navigator(); // changed to interface type
Context context();
//...
}
ApplicationModule.class
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
BaseNavigator provideNavigator(Navigator navigator) {
return navigator;
} // this will return interface type but with implementation you needed
#Provides
#Singleton
Context provideApplicationContext() {
return this.application;
}
}
Navigator.class
#Singleton
public class Navigator implements BaseNavigator {
#Inject // don't forget to add this annotation to the constructor
public Navigator() {}
}
BaseActivity.class
public abstract class BaseActivity extends Activity {
#Inject
BaseNavigator navigator;// changed to interface type
//code here
}
Now you did not create new instance for Navigator, Dagger did it instead of you in its generated factory.
I have 2 modules: AndroidModule for providing Context-related objects and PhotosModule providing PhotoManager.
I want to use PhotoManager in PhotosFragment. PhotoManager depends on DbManager, which is provided by AndroidModule.
How do I connect all the dots?
The code compiles, but a NPE is thrown on this line:
mDbManager.readDatabase();
meaning that the injection of DbManager into PhotoManager did not occur, even though I set the
injects = PhotoManager.class
in AndroidModule.
Also, mDbManager is not a private field, so I don't think I have to use injection on PhotoManager's constructor; field injection should be sufficient.
What's the reason and how do I satisfy the missing dependency? I assume there may be more than one problem with my code. Note: I just started with Dagger, please keep that in mind.
Application:
public class TestApp extends Application {
private ObjectGraph mObjectGraph;
#Override
public void onCreate() {
super.onCreate();
mObjectGraph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new PhotosModule());
}
public void inject(Object object) {
mObjectGraph.inject(object);
}
}
AndroidModule:
#Module(library = true, injects = PhotoManager.class)
public class AndroidModule {
private final TestApp mApplication;
public AndroidModule(TestApp application) {
mApplication = application;
}
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return mApplication;
}
#Provides
#Singleton
DbManager provideDbManager() {
return new DbManager(mApplication);
}
}
PhotosModule:
#Module(injects = PhotosFragment.class)
public class PhotosModule {
#Provides
#Singleton
PhotoManager providePhotoManager() {
return new PhotoManager();
}
}
PhotoManager:
#Singleton
public class PhotoManager {
#Inject
DbManager mDbManager;
public void doSomething() {
mDbManager.readDatabase();
}
}
PhotosFragment:
public class PhotosFragment extends Fragment {
#Inject
PhotoManager mPhotoManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((TestApp) getActivity().getApplication()).inject(this);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
///...
mPhotoManager.doSomething();
}
}
DbManager:
#Singleton
public class DbManager {
public DbManager(Context context) {
//...
}
public void readDatabase() {
//...
}
}
Here's the core of the problem: in PhotosModule, you have the provider method:
PhotoManager providePhotoManager() {
return new PhotoManager();
}
You are newing up the PhotoManager yourself, so Dagger doesn't have an opportunity to inject its dependency (the DbManager).
I think it's because you're misunderstanding the meaning of the injects property on the #Module(injects=...) annotation of the AndroidModule. You have:
#Module(library = true, injects = PhotoManager.class)
But that injects isn't needed, because you're never calling objectGraph.inject on the PhotoManager. Instead, you're only injecting the PhotoFragment, which is correct...
So one way to fix the NPE problem:
remove the injects=PhotoManager.class from AndroidModule.java:
#Module(library = true)
change PhotosModule to include AndroidModule:
#Module(injects = PhotosFragment.class, includes = AndroidModule.class)
I avoid field injection using the #Inject annotation (only use it for the top-level objects, i.e. where you do the objectgraph.inject, such as activity and fragment level), and use constructor injection instead for all the other dependencies. So I'd change PhotoManager to inject the DbManager like this:
private final DbManager mDbManager;
public PhotoManager(DbManager dbManager) {
mDbManager = dbManager;
}
I've put up the code here on GitHub.