error: [Dagger/IncompatiblyScopedBindings] (unscoped) may not reference scoped bindings:

Viewed 7172

I don't understand how to resolve this error. I got this error after trying to add fragments to my application and using Dagger for DI. Here is the error stack:

error: [Dagger/IncompatiblyScopedBindings] di.component.ApplicationComponent (unscoped) may not reference scoped bindings: @Provides @di.ApplicationContext @di.ApplicationScope android.content.Context di.Module.ApplicationContextModule.getApplicationContext(application.MyApplication) @Provides @di.ApplicationScope android.content.SharedPreferences di.Module.SharedPreferencesModule.getSharedPreferences(@di.ApplicationContext android.content.Context) @Provides @di.ApplicationScope service.KeyStoreServiceInterface di.Module.KeyStoreModule.getKeyStoreService(@Named("KEY_STORE_FILE") java.io.File) @Provides @di.ApplicationScope repository.SharedPreferencesHelper di.Module.SharedPreferenceHelperModule.getSharedPreferencesHelper() @Provides @di.ApplicationScope service.CoinmarketcapService di.Module.CoinmarketcapModule.getCoinmarketcapService(com.google.gson.Gson, okhttp3.OkHttpClient) @Provides @di.ApplicationScope com.google.gson.Gson di.Module.GsonModule.getGson() @Provides @di.ApplicationScope okhttp3.OkHttpClient di.Module.OkHttpModule.getOkHttpClient() @Provides @di.ApplicationScope repository.WalletRepositoryInterface di.Module.WalletRepositoryModule.getWalletRepository(repository.SharedPreferencesHelper, service.KeyStoreService)

Here is my ApplicationComponent class:

@Component(modules = {ApplicationContextModule.class,
        SharedPreferencesModule.class,
        KeyStoreModule.class,
        SharedPreferenceHelperModule.class,
        AndroidInjectionModule.class,
        BindModule.class,
        AndroidSupportInjectionModule.class,
        OkHttpModule.class,
        GsonModule.class,
        CoinmarketcapModule.class,
        WalletRepositoryModule.class})
@SuppressWarnings("unchecked")
public interface ApplicationComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(MyApplication myApplication);
        ApplicationComponent build();
    }

    void inject(MyApplication myApplication);

    @ApplicationContext
    Context getApplicationContext();

    SharedPreferences getSharedPreferences();

    KeyStoreServiceInterface getKeyStoreService();

    SharedPreferencesHelper getSharedPreferencesHelper();

    CoinmarketcapService getCoinmarketcapService();

    WalletRepositoryInterface getWalletRepository();

}

I have a FragmentScope and an ActivityScope annotation in my android code. It's just regular scopes for Dagger with Retention.RUNTIME. Here is my application code:

public class MyApplication extends MultiDexApplication implements HasActivityInjector, HasFragmentInjector {

    private ApplicationComponent applicationComponent;

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    @Override
    public void onCreate() {

        super.onCreate();

        DaggerApplicationComponent
                .builder()
                .application(this)
                .build()
                .inject(this);

        Timber.plant(new Timber.DebugTree());
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

    public ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }

    @Override
    public AndroidInjector<Fragment> fragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}

Any help would be appreciated.

EDIT: I managed to solve this by adding @ApplicationScope to my component. Why do I have to do this? Before adding fragments and @FragmentScope to my code (Before this I had only @activityscope and @applicationscope) I did not have this issue, it only appeared after adding fragments? If anyone can help answer this, it is still worth to accept that answer to help anyone else that might have this problem and wants to understand it.

2 Answers

You haven't shown us ApplicationContextModule, but from your error message it probably contains this:

@Provides @ApplicationContext @ApplicationScope
Context getApplicationContext(MyApplication application) {
  return application.getApplicationContext();
}

You've annotated this @Provides method with @ApplicationScope, which instructs Dagger to save the returned Context within a Component—specifically, the component that you've also annotated with @ApplicationScope. Before you made the change in your edit, there was no matching @ApplicationScope, and Dagger gave you that message. Now that you have one, Dagger knows where to store the saved Context instance.

Confusingly Generously, Dagger won't object to inappropriate bindings that you aren't using yet, so Dagger won't object to your lack of a component scope annotation until you start using bindings in that scope, which is what probably happened around the same time you introduced Fragment scope.

See also the Dagger User's Guide:

Since Dagger 2 associates scoped instances in the graph with instances of component implementations, the components themselves need to declare which scope they intend to represent. For example, it wouldn’t make any sense to have a @Singleton binding and a @RequestScoped binding in the same component because those scopes have different lifecycles and thus must live in components with different lifecycles. To declare that a component is associated with a given scope, simply apply the scope annotation to the component interface.

Notably, since the instance of Application doesn't change over the lifetime of the component either and the value of getApplicationContext is not expected to change over the lifetime of the Application. That means that your scope isn't really giving you much, other than avoiding repeated calls to your getApplicationContext method in ApplicationContextModule.


"But wait," I hear you think. "Why wouldn't Dagger know that my @ApplicationScoped binding belongs on my ApplicationComponent? After all, Dagger sees ApplicationContextModule installed on ApplicationComponent, so the only way that makes sense is if ApplicationComponent is implicitly @ApplicationScoped." Two reasons: First, in a sense, this is forced documentation that also helps Dagger be more clear about which binding is erroneous so you don't accidentally install @ActivityScoped bindings in your ApplicationComponent directly and convince Dagger that your Component is simultaneously application-scoped and activity-scoped. Second, you may also annotate an injectable class with a scope annotation, and Dagger wouldn't be able to infer anything because there's no component-installs-module relationship that it could read. Between those two, Dagger forces you to annotate your Component in the docs, I quoted above.

I had the same issue, but after deleting the module providing Context from ApplicationComponent problem is gone.

Related