Android为多个Dagger2模块使用单个上下文

mah*_*guy 5 android dependency-injection dagger-2

我是Dagger2在Android 上使用的新手.我创建一些类作为他们正在使用的Dagger模块context,我不能合并,组合或使用单个上下文用于他们需要的其他模块.我现在得到这个错误:

android.content.Context is bound multiple times
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

零件:

@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);
}
Run Code Online (Sandbox Code Playgroud)

然后在Application类中创建Dagger2:

   component = DaggerApplicationComponent.builder()
           .appModules(new SpModules(this))
           .build();
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个问题?

Fre*_*red 4

这只是匕首,告诉您您正在为组件提供多次相同的类。事实上,您可以在这里以及跨不同模块对同一组件执行此操作:

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

@Provides 
Context provideContext() {
    return this.context;
}
Run Code Online (Sandbox Code Playgroud)

这些方法是不同的,但它们提供相同的依赖关系,并且没有额外的信息,dagger 无法确定Context使用哪个。

你有几个选择。如果单个上下文足以为您的所有类提供服务,那么您可以简单地删除额外的提供方法。

但是,假设您需要应用程序和其他一些上下文。您可以使用注释Named。它的工作原理如下,您使用此注释来注释您的提供方法,这基本上将为依赖项提供一个名称。就像这样:

@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();
   }

   //...
}
Run Code Online (Sandbox Code Playgroud)

现在,当您使用这些依赖项时,您还需要进行注释,例如,假设您有一个依赖于应用程序上下文的对象:

public class Something {
   public Something(@Named("application.context") Context context) {
     //...
   }
}
Run Code Online (Sandbox Code Playgroud)

或者作为一个字段:

public class Something {
   @Named("application.context") Context context;
   // ...
}
Run Code Online (Sandbox Code Playgroud)

或者甚至在你的模块中:

@Provides
@Singleton
SP provideSharePreferences(@Named("context") Context context) {
    return new SP(context);
}
Run Code Online (Sandbox Code Playgroud)

名称可以是您想要的任何名称,只要它们一致即可。

另一种方法是使用Qualifiers. 它们的工作方式与Named注释类似,但有所不同。您将限定依赖性。假设您创建了 2 个限定符:

@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 {
}
Run Code Online (Sandbox Code Playgroud)

然后您可以使用它们来注释提供方法:

@Module
public class SpModules {
   // ...

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

   //...
}

@Module
public class RealmModule {
   //...

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

   //...
}
Run Code Online (Sandbox Code Playgroud)

然后您可以按照与名称相同的方式使用它。以下是示例:

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);
}
Run Code Online (Sandbox Code Playgroud)

同样,这些名称可以是任何名称。我个人并没有花太多精力去思考它们。希望这可以帮助。