Weiyi
Weiyi

Reputation: 1933

Why Dagger2 inject the same object but with 2 different instances?

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

Upvotes: 0

Views: 1396

Answers (1)

Fred
Fred

Reputation: 17085

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

Upvotes: 1

Related Questions