ArticlesContract.Presenter is a new instance in Adapter, which is different with ArticleListFragment, so my data was lost ! I have no idea why I got two different instances:
#Module
public class ArticleListFragmentModule {
#Provides
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
public class ArticleListFragment extends DaggerFragment implements ArticlesContract.View {
#Inject
ArticlesContract.Presenter mPresenter; //one instance
}
public class ArticlesAdapter extends RecyclerView.Adapter<ArticleViewHolder> {
#Inject
ArticlesContract.Presenter mPresenter; //another different instance
}
2018-05-16 UPDATED: this issues is fixed by following #Fred answer : lack a scope and managing this scope:
#Scope
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface ArticlesScope {
}
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = ArticleListFragmentModule.class)
#ArticleListScope
abstract ArticleListFragment bindArticleListFragment();
}
#Module
public class ArticleListFragmentModule {
/**
* Provide dependency for interface.
* Interface cannot be annotated with #Inject, otherwise it will cause, error: ArticlesContract.Presenter cannot be provided without an #Provides- or #Produces-annotated method.
*/
#Provides
#ArticleListScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
Scoping with #ContributesAndroidInjector, refer to Dagger 2 Annotations: #Binds & #ContributesAndroidInjector
This is because you lack a scope and managing this scope. You need to create a scope yourself or use one provided by dagger already. Here since it's a presenter it can be the Reusable scope I guess or Singleton. However, Singleton has a performance impact that might not be desirable.
The next important thing is that you need to understand that while the component's instance is the same the provided binding will be the same (excluding the case of the Reusable scope). In other words, scopes provide a way of telling dagger - "while this component is alive and it's scope is X then all instances scoped with X will be the same". Here's what I mean in code:
#Scope
#Documented
#Retention(RUNTIME)
public #interface PresenterScope {}
#Module
public class ArticleListFragmentModule {
#Provides
#PresenterScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
I don't know how you've set up your component, but you'd have to annotate it also with the PresenterScope. Now, it's just a matter of making sure that when you inject ArticleListFragment and ArticlesAdapter you'll be using the same instance of the component. If you rebuild the component than the instance of the presenter will be different.
Remember that Reusable is a bit different, but it should suit your needs here since the presenter should hold no state.
Hope this helps
Related
Im learning and newer to Dagger2 and stuck in the issue where I need to use both #Provides and #Binds in a Module. but its giving error
[Dagger/DependencyCycle] Found a dependency cycle:
public interface CarComponent{
^
com.stupido.daggertutorial.Engine is injected at
com.stupido.daggertutorial.CarModule.bindsEngine(arg0)
com.stupido.daggertutorial.Engine is injected at
com.stupido.daggertutorial.CarModule.providesCar(engine, …)
com.stupido.daggertutorial.Car is requested at
com.stupido.daggertutorial.CarComponent.getCar()
before I was using #Provides it worked fine but with combination I get above issue.
Component
#Component(modules = CarModule.class)
public interface CarComponent{
//constructor injection
Car getCar();
}
Module
#Module
public abstract class CarModule {
#Binds
abstract Wheels bindsWheels(Wheels wheels);
#Binds
abstract Engine bindsEngine(Engine engine);
#Provides
static Car providesCar(Engine engine,Wheels wheels){
return new Car(wheels,engine);
}
}
Activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CarComponent carComponent = DaggerCarComponent.create();
Car car = carComponent.getCar();
}
}
#Binds is usually for the case when you have some inherited class and its interface and you want dagger to know this and be able to inject the interface. Usually you'd have
#Binds
abstract Engine bindsEngine(DieselEngine dieselEngine);
and then you can just inject engine without knowing the implementation detail (it's diesel engine).
In your case, if you remove both #Binds methods, it should start working (if there's no other issue).
You are using #Binds in the wrong way. Please read their documentation.
A #Binds method:
Must have a single parameter whose type is assignable to the return type. The return type declares the bound type (just as it would for a #Provides method) and the parameter is the type to which it is bound.
You have to change #Binds to #Provides.
I'm confused about scoped dependencies in Dagger using dagger-android.
Using #ContributesAndroidInjetor I have a code something like the following:
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = PotatoesModule.class)
public abstract MainActivity contributeMainActivityInjector();
#ContributesAndroidInjector
public abstract UserActivity contributeUserActivity();
}
The ActivityBindingModule is defined as a module in my AppComponent. But the problem is. How can I do something like
#UserScope
#Component(dependencies = AppComponent.class)
public interface UserComponent {...}
And annotate an Activity to use that scope? Is all my dependencies inside activity "local singletons"? Because each Activity injector is a subcomponent of AppComponent.
Maybe I'm not understanding the concept of "scopes" using dagger-android, I would be glad if someone could explain it.
Here's some clarification on scopes:
Say you had an AppComponent and you Annotate it with the #Singleton annotation:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class
})
public interface AppComponent extends AndroidInjector<BaseApplication> {
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
And you had an AppModule which provide App level dependencies (i.e. a Retrofit Instance for example that you annotate with #Singleton):
#Module
public class AppModule {
#Singleton
#Provides
static Retrofit provideRetrofitInstance(){
return new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
Then we can say that AppComponent owns the #Singleton scope and therefore the #Singleton annotation that you put on the Retrofit instance that you provided now has the same scope as the AppComponent - i.e. it's an application level scope.
If you want to scope to Activities - you should make a custom scope like this:
#Scope
#Documented
#Retention(RUNTIME)
public #interface UserScope {
}
Then in your ActivityBindingModule (that you've written), annotate the UserActivity with #UserScope if you want the UserActivity to "own" the #UserScope scope. Also, add a module next to the #ContributesAndroidInjector - let's call it UserModule.class:
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = PotatoesModule.class)
public abstract MainActivity contributeMainActivityInjector();
#UserScope
#ContributesAndroidInjector(modules = UserModule.class)
public abstract UserActivity contributeUserActivity();
}
And now, creating UserModule.class and annotating a provided dependency with #UserScope:
#Module
public class UserModule {
#UserScope
#Provides
static User provideUser(){
return new User();
}
}
This dependency now has the same scope as the UserActivity. So when UserActivity is destroyed and re-created, the dependency provided will also be destroyed and recreated.
To finish up:
Create a POJO User:
public class User {
public User() {
}
}
and now, if you go to your UserActivity and do:
public class UserActivity extends DaggerAppCompatActivity {
private static final String TAG = "UserActivity";
#Inject
User aUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
Log.d(TAG, "onCreate: " + aUser);
}
}
If you run your App now you will see a memory address being printed to the log. Rotate the device to destroy and re-create the activity and you'll see that the memory address changes. This is how we know that the #UserScope is working correctly.
If you want to see your Application scope in action i.e. #Singleton, then create an AppModule, add it to your AppComponent and provide a User dependency in that module and annotate it with #Singleton. Remember to use the #Named annotation too since you now have 2 dependencies that are provided with the same return type (that can both be accessed within the Activity Scope).
Go to your UserActivity again and Inject both Users (remember to use #Named). Log it in another logging statement and after rotating the device you will notice you have the same memory address for the Application scoped dependency.
I hope this cleared things up.
Is all my dependencies inside activity "local singletons"? Because each Activity injector is a subcomponent of AppComponent.
The subcomponents generated by dagger-android are unscoped, unless you annotate the #ContributesAndroidInjector-annotated method with a scope.
But the problem is. How can I do something like ... #Component(dependencies = AppComponent.class) ... And annotate an Activity to use that scope?
As far as I know, you can only use subcomponents with dagger-android. Furthermore activity subcomponents must be declared in modules installed in the application component, while fragment subcomponents may be declared in modules installed in either application, activity or fragment components.
I'm not sure what you mean by "annotate an Activity to use that scope" though.
I think I'm missing something. I get this error:
PostsVM cannot be provided without an #Inject constructor or from an
#Provides-annotated method.
Assume classes as follows :
#Module
public class AppModule {
private final Application mApplication;
#Singleton
#Provides
ViewModel provideListViewModel() {
return new PostsVM();
}
}
And a class PostVM
#Singleton
public class PostsVM extends ViewModel {
public boolean get(){
return true;
}
}
And a component :
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
}
And in activity :
#Inject
public ViewModelProvider.Factory factory;
#Override
protected void onCreate(Bundle savedInstanceState) {
InjectorClass.inject(this);
As you can see, the example given for PostVM class, does't depend on anything, why do I need an #inject constructor in it?
tl;dr To prevent errors and to follow convention.
From the JavaDoc of #Inject you can read:
Injectable constructors are annotated with #Inject and accept zero or more dependencies as arguments. #Inject can apply to at most one constructor per class.
And it's always good practice to follow the convention / documentation.
So #Inject marks an entry point for Dagger to signal it how and where to create your class. It is a clear sign of how you intend your class to be used.
What if you have multiple constructors?
What if you require additional setup and should use a #Module instead?
By just defaulting to a no-args constructor (if possible) things could start breaking very easily and you might not be able to pinpoint the source easily if you just assume Dagger does its job.
__ cannot be provided without an #Inject constructor or from an #Provides-annotated method.
This error on the other hand gives you a strong signal that you're missing something and cannot be ignored.
According to tutorials like this one: in Dagger2 we are able to provide local singleton objects using a custom scope.
I already have a global appComponent and my goal is to create a activity scope that allows a subcomponent to have local singleton providers.
My issue is when i create a custom scope and inject my activity twice, i see that the object being provided is not the same one, even though i tagged it with the custom scope. for the appComponent which uses the #Singleton annotation it works fine though.
Lets take a look at how i did this:
Here is the custom activity scope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
And here is how i define the subcomponent:
#ActivityScope
#Subcomponent(modules = {PresenterModule.class, UseCaseModule.class})
public interface ActivitySubComponent {
void inject(LoginActivity target);
void inject(LoginPresenter target);
void inject(UCGetFireBaseAccount target);
}
Notice here that i've tagged this subcomponent with #ActivityScope annotation that i custom made above.
Now lets see the appComponent which is the parent component and global:
#Singleton
#Component(modules = {AppModule.class, NetworkModule.class, RepositoryModule.class})
public interface AppComponent {
void inject(myappCloudRepo target);
void inject (FireBaseCloudRepo target);
ActivitySubComponent plus(PresenterModule presenterModule, UseCaseModule useCaseModule);
}
Notice ActivitySubComponent is defined here as a subcomponent.
Finally lets look at the presenterModule class to see what i wanted to be provided as a singleton:
#Module
public class PresenterModule {
#Provides
#ActivityScope
LoginPresenter provideLoginPresenter(Context context) {
return new LoginPresenter(context);
}
}
Now when i actually use this i set it up in my application class like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(MyApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
}
Then lastly in any activity i do this:
public class LoginActivity extends AppCompatActivity{
ActivitySubComponent activityComponent;
//this loginpresenter is from the activitysubcomponent
#Inject
LoginPresenter loginPresenter;
//this retrofit object is from the appcomponent
#Inject
Retrofit retrofit;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue",loginPresenter.toString());
Log.d("j2emanue",retrofit.toString());
activityComponent= ((MyApplication)getApplication()).getActivityComponent();
activityComponent.inject(this);
Log.d("j2emanue2",loginPresenter.toString());
Log.d("j2emanue2",retrofit.toString());
}
}
When i take a look at the logs after injecting TWICE i am expecting all objects to be the same but instead the loginPresenter is another instance but the retrofit is the same so that works.
Here is the log:
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#1c27a460
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue: retrofit2.Retrofit#13366d19
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: com.myapp.mobile.myappfashion.UI.Presenters.LoginPresenter#3dc041de
05-23 23:33:35.617 31298-31298/com.myapp.mobile.myappfashion D/j2emanue2: retrofit2.Retrofit#13366d19
Notice the loginpresenter is another object so its not making a local singleton for me that would only be destroyed when i dereference the activitysubcomponent.
what am i missing here ? The reason i want it to be a local singleton is for configuration changes. This way the presenter can be maintained during a config change.
In your application class you create and return a new subcomponent.
public ActivitySubComponent getActivityComponent() {
return appComponent.plus(new PresenterModule(),new UseCaseModule());
}
Now scopes mean that within a scope an object exists only one time. But in your case, you create 2 different components. Those 2 components will share everything in their parent component (they are subcomponents), but anything within their scope will be recreated, but unique to their scope.
Anything within your component will use the same #ActivityScope annotated objects, but if you create 2 components you will have 2 copies of everything.
If you take #Singleton as an example, it does not mean the object will be an actual Singleton. It is the name of the scope that you are supposed to have for your root component, which should only be created once and be kept during the lifetime of the application.
If you were to create 2 AppComponents that are #Singleton you could observe that same behavior—two different instances of your object.
In your example Retrofit is the same, because you use the same AppComponent both times, but LoginPresenter gets recreated along with every ActivitySubComponent that you create.
With Dagger you should try that your components follow the same lifecycle as what they scope, thus your app should hold an AppComponent, and every Activity should have their own ActivityComponent (keep the component as a member variable!). When you create a new Activity you should create a new #ActivityScope scoped component, but not more often than that.
You should remove your getActivityComponent() from the Application and keep a reference to your ActivitySubComponent, because injecting scoped dependencies with the same component will give you the same objects.
activityComponent.inject(this);
activityComponent.inject(this);
// call it as many times as you'd like.
activityComponent.inject(this);
Just don't recreate your component.
I am not quite sure how to solve this with dagger 2.
Lets assume we have ApplicationModule that provides us ApplicationContext
then we have ApplicationComponent that uses just this one module.
Then on top of it we have ActivityModule and ActivityComponent that has dependency on ApplicationComponent.
ActivityComponent is build just like
ApplicationComponent component = ((MyApplication) getApplication()).getComponent();
mComponent = Dagger_ActivityComponent.builder()
.applicationComponent(component)
.activityModule(new ActivityModule(this))
.build();
And then I inject my activity:
mComponent.inject(this);
Now I am able to use everything that is declared inside my ActivityModule, however it is not possible for me to access ApplicationModule.
So the question is how could that be achieved? So that when I build component that depends on another component I can still access module from the first one?
EDIT
I think I have found solutions, after rewatching Devoxx talk by Jake again, I have had to miss that out, whatever I want to use from another components module I have to provide in that component, for example I want to use Context from ApplicationModule then inside ApplicationComponent I have to state Context provideContext(); and it is going to be available. Pretty cool :)
You have already answered your question, but the answer is to specify the provision methods in your "superscoped" component (ApplicationComponent).
For example,
#Module
public class ApplicationModule {
#Provides
#Singleton
public Something something() {
return new Something.Builder().configure().build();
// if Something can be made with constructor,
// use #Singleton on the class and #Inject on the constructor
// and then the module is not needed
}
}
#Singleton
#Component(modules={ApplicationModule.class})
public interface ApplicationComponent {
Something something(); //PROVISION METHOD. YOU NEED THIS.
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
#ActivityScope
public class OtherThing {
private final Something something;
#Inject
public OtherThing(Something something) {
this.something = something;
}
}
#Component(dependencies = {ApplicationComponent.class})
#ActivityScope
public interface ActivityComponent extends ApplicationComponent { //inherit provision methods
OtherThing otherThing();
}