Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Using single context for multiple Dagger2 modules

Tags:

I'm newbie to use Dagger2 on android. i create some class as Dagger module which they are using context, i can't merge,combine or using single context for other modules which they need that. and i get this error now:

android.content.Context is bound multiple times

SpModules:

@Module
public class SpModules {
    private Context context;

    public SpModules(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
    }
}

RealmModule:

@Module
public class RealmModule {
    private Context context;

    @Provides
    Context provideApplicationContext() {
        return AlachiqApplication.getInstance();
    }

    @Provides
    @Singleton
    RealmConfiguration provideRealmConfiguration() {
        final RealmConfiguration.Builder builder = new RealmConfiguration.Builder()
                .schemaVersion(Migration.SCHEMA_VERSION)
                .deleteRealmIfMigrationNeeded()
                .migration(new Migration());
        return builder.build();
    }

    @Provides
    Realm provideDefaultRealm(RealmConfiguration config) {
        return Realm.getInstance(config);
    }

    @Provides
    Context provideContext() {
        return this.context;
    }
}

Component:

@Component(modules = {RealmModule.class, SpModules.class})
@Singleton
public interface ApplicationComponent {
    void inject(ActivityRegister target);

    void inject(ActivityMain target);

    void inject(ActivityBase target);

    void inject(FragmentAlachiqChannels target);

    void inject(SocketServiceProvider target);
}

and then Application class to make Dagger2:

   component = DaggerApplicationComponent.builder()
           .appModules(new SpModules(this))
           .build();

how can i resolve this problem?

like image 250
mahdi pishguy Avatar asked Oct 25 '16 10:10

mahdi pishguy


1 Answers

That's simply dagger telling you that you're providing several times the same class to your component. In fact you do it here as well as across different modules for the same component:

@Provides
Context provideApplicationContext() {
    return AlachiqApplication.getInstance();
}

@Provides 
Context provideContext() {
    return this.context;
}

These methods are different, but they're providing the same dependency and without extra info dagger is not able to determine which Context to use.

You got a couple of options. If a single context would be enough to serve all your classes, then you could simply remove the extra provide methods.

However, let's say you need both application and some other context. You can use the annotation Named. Here's how it works, you annotate your provide methods with this annotation which basically will give a name to the dependency. Like so:

@Module
public class SpModules {
   // ...

   @Provides
   @Named("context")
   Context provideContext() {
      return this.context;
   }

   //...
}

@Module
public class RealmModule {
   //...

   @Provides
   @Named("application.context")
   Context provideApplicationContext() {
      return AlachiqApplication.getInstance();
   }

   //...
}

Now you need to also annotated when you use these dependencies, for example say you have an object that depends on the application context:

public class Something {
   public Something(@Named("application.context") Context context) {
     //...
   }
}

Or as a field:

public class Something {
   @Named("application.context") Context context;
   // ...
}

Or even in your module:

@Provides
@Singleton
SP provideSharePreferences(@Named("context") Context context) {
    return new SP(context);
}

The name can be anything you want as long as they're consistent.

An alternative is to use Qualifiers. They work similar to the Named annotation, but are different. You'll be qualifying the dependency. So say you create 2 qualifiers:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(RUNTIME)
@javax.inject.Qualifier
public @interface InstanceContext {
}

@java.lang.annotation.Documented
@java.lang.annotation.Retention(RUNTIME)
@javax.inject.Qualifier
public @interface ApplicationContext {
}

You can then use these to annotate the provide methods:

@Module
public class SpModules {
   // ...

   @Provides
   @InstanceContext
   Context provideContext() {
      return this.context;
   }

   //...
}

@Module
public class RealmModule {
   //...

   @Provides
   @ApplicationContext
   Context provideApplicationContext() {
      return AlachiqApplication.getInstance();
   }

   //...
}

You then use it the same way as with name. Here are the examples:

public class Something {
   public Something(@ApplicationContext Context context) {
     //...
   }
}

public class Something {
   @ApplicationContext Context context;
   // ...
}

@Provides
@Singleton
SP provideSharePreferences(@InstanceContext Context context) {
    return new SP(context);
}

Again, these names can be anything. I personally didn't put a lot of effort in thinking about them. Hope this helps.

like image 67
Fred Avatar answered Sep 22 '22 16:09

Fred