Dagger: No injectable members on android.content.Context. Do you want to add an injectable constructor? required by Class for Module

StackOverflow https://stackoverflow.com/questions/23317254

سؤال

I am trying to convert my singleton CookieUtil to be injected with Dagger into LoginActivity. CookieUtil takes an application context therefor I have set up the following structure :

AndroidModule

@Module(
        injects = {
            CookieUtil.class,
            LoginActivity.class
        },
        library = true
)
public class AndroidModule {
    private final App application;

    public AndroidModule(App application) {
        this.application = application;
    }

    /**
     * Allow the application context to be injected but require that it be annotated with
     * {@link ForApplication @Annotation} to explicitly differentiate it from an activity context.
     */
    @Provides
    @Singleton
    @ForApplication
    Context provideApplicationContext() {
        return application;
    }

    @Provides
    @Singleton
    CookieUtil provideCookieUtil() {
        return new CookieUtil();
    }
}

CookieUtil (What I want to inject into LoginActivity)

@Singleton
public class CookieUtil {
    @Inject Context mContext; // gets injected with a app context. is this right ?

    private PersistentCookieStore persistentCookieStore;

    private CookieUtil() {
      // use injected mContext
      persistentCookieStore = new PersistentCookieStore(mContext);
      // ...
    }
}

LoginActivity (Where I want to inject CookieUtil)

public class LoginActivity extends BaseActivity { @Inject CookieUtil cookieUtil;

@Override
protected void onCreate(Bundle savedInstanceState) {
  // use CookieUtil ...
}

}

I have also set up all the bootstraping stuff from Dagger examples to enable everything to work

BaseActivity

public class BaseActivity extends ActionBarActivity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Perform injection so that when this call returns all dependencies will be available for use.
        ((App) getApplication()).inject(this);
    }
}

App

public class App extends Application {
  private ObjectGraph graph;

  public void onCreate() {
    super.onCreate();
    graph = ObjectGraph.create(getModules().toArray());
    // ...
  }

  protected List<Object> getModules() {
    return Arrays.asList(
      new AndroidModule(this),
      new CupsModule() // Another module I haven't listed
    );
  }

  public void inject(Object object) {
      graph.inject(object);
  }

}

ForApplication

@Qualifier
@Retention(RUNTIME)
public @interface ForApplication {
}

When I run the code, I get

No injectable members on android.content.Context. Do you want to add an injectable constructor? required by CookieUtil for AndroidModule

What am I doing wrong ?

Everything still looks like magic to me as I don't fully aware of how to wire everything up, so detailed explanation would be very much appreciated.

Also, would be helpful if someone could point me to an explanation on dagger modules, when does it make sense to separate into two different modules ? What logical pieces do they usually bind ?

Thanks

EDIT

Changed suggested by Christian Gruber

@Singleton
public class CookieUtil {
    private Context mContext;
    private PersistentCookieStore persistentCookieStore;

    @Inject
    public CookieUtil(Context context) {
          // use injected context
          mContext = context
          persistentCookieStore = new PersistentCookieStore(mContext);
          // ...
    }
}
هل كانت مفيدة؟

المحلول

Cookie cannot have a private constructor and still be created (provisioned) by Dagger. You can have a package-friendly constructor, and then it should work without @Provides CookiUtil .... Having the provides method for a class you control and could make injectable seems wasteful.

Generally speaking, Dagger considers a "binding" according to a "key" which is its type (with concrete type parameters, such as Foo<Bar>) along with any @Qualifier. So the type Foo<Bar> is different from @ForApplication Foo<Bar>. A binding is requested wherever @Inject occurs, and is supplied wherever @Provides occurs (or for unqualified bindings, if a class has an @Inject-marked constructor or fields, then an implicit binding is supplied. For every @Inject field or constructor parameter there must be a present binding for that key. Dagger needs to be able to see the methods it uses to create things, so generally private fields, methods, and constructors are not possible.

Also, please never simply inject Context without a qualifier. Or better, inject Application or Activity if that's the Context subtype you mean. Your graph WILL be impossible to manage if you don't distinguish between the 30,000 things that Android calls Context subtypes. (snark)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top