3

SOLVED

I am new with Dagger 2 and I am trying to provide Activity Context to classes but without success. I search a lot but did not find any appropriate answer.

I can provide Application Context. But I also need to provide Activity Context as well and I don't know any good way to implement that.

I need to clarify that I am using Dagger for Android dependencies.

def dagger_version = "2.24"
implementation "com.google.dagger:dagger:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"

I also have only an AppComponent with the following code:

@Singleton
@Component(
        modules = {
                AndroidSupportInjectionModule.class,
                ActivityBuildersModule.class,
                AppModule.class,
                ViewModelFactoryModule.class,
        }
)
public interface AppComponent extends AndroidInjector<BaseApplication> {

    SessionManager sessionManager();

    @Component.Builder
    interface Builder{

        @BindsInstance
        Builder application(Application application);

        AppComponent build();
    }
}

Except that I have a module for each of my activity but I didn't find a way to inject the Activity Context either for AppComponent or from a ActivityModule.

What is the right way to do that?

UPDATE

I finally found the right way to do that.

First I created a module for the class that I want to provide

@Module
public class AlertsModule {

    @Provides
    static Alerts provideAlerts(Activity activity){
        return new Alerts(activity);
    }

}

Then I go to the ActivityModules that I want to Inject that custom Class and do a Binding like that

@Module
public abstract class MainActivityModule {

    ...

    @Binds
    abstract Activity providesActivity(MainActivity activity);

    ...

}

And finally I just include the CustomClassModule in my ActivityBuildersModule where I use @ContributesAndroidInjector to provide my Activities.

    @MainScope
    @ContributesAndroidInjector(
            modules = {
                    AlertsModule.class,
            }
    )
    abstract MainActivity contributeMainActivity();
DimKe
  • 43
  • 6

3 Answers3

3

You can bind an instance of Activity in the same way you're currently binding an instance of Application, either by using an @Component.Builder or an @Component.Factory.

An example implementation would look something like this:

@Subcomponent(...)
interface ActivitySubcomponent {

    @Subcomponent.Factory
    interface Factory {
        ActivitySubcomponent create(@BindsInstance MyActivity activity)
    }
}
@Module(subcomponents = [ActivitySubcomponent.class])
class ApplicationModule {
    ....
}
public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        ((MyApplication) getApplication())
            .applicationComponent
            .activitySubcomponentFactory()
            .create(this)
            .inject(this)
    }
}
Sandi
  • 2,415
  • 1
  • 13
  • 11
  • I couldn't make it work. I created an ActivitySubcomponent as you said. I included it to my AppModule class as a subcomponent. That's ok so far. Now I want to pass the Activity Context through constructor in the AppModule. e.x. static MyClass provideMyClass(Context context) and I get errors on execution – DimKe Oct 14 '19 at 12:34
  • You can't pass an `Activity` to a class that requires it in your `AppModule`. If the class requires an `Activity` as a dependency, you should most likely provide that class from your `ActivityModule` instead and scope your class to the lifecycle of the `Activity`, otherwise you might run into memory leaks. – Sandi Oct 14 '19 at 12:41
  • Better to Complete this Answer with this Guide https://codelabs.developers.google.com/codelabs/android-dagger/index.html?index=..%2F..index#9 – Elias Fazel Feb 09 '20 at 01:26
0
@Module
class MainActivityModule(mActivity: MainActivity) {

    var mActivity: MainActivity


    init {
        this.mActivity= mActivity
    }


    @Singleton
    @Provides
    fun getMainActivity(): MainActivity{
        return mActivity
    }


}

now add this module to your Component class and pass your activity to module constructor while using your component builder hope this may helps you.

Atif AbbAsi
  • 5,633
  • 7
  • 26
  • 47
0

@Named annotation helps us to differentiate the Context. We can differentiate the method context() in ActivityModule and ContextModule by adding the @Named annotation as below:

@Module
public class ActivityModule {
    private final Context context;

    ActivityModule(Activity context){
        this.context = context;
    }

    @Named("activity_context")
    @Provides
    public Context context(){ return context; }
}

@Module
public class ContextModule {
    private final Context context;

    ActivityModule(Activity context){
        this.context = context;
    }

    @Named("application_context")
    @Provides
    public Context context(){ return context.getApplicationContext(); }

Then, we tell Dagger to use the respective Context, as below:

@Module(includes = ContextModule.class)
public class OkHttpClientModule {
    ....
    @Provides
    public File file(@Named("application_context") Context context){
    File file = new File(context.getCacheDir(), "HttpCache");
    file.mkdirs();
    return file;
    }
    ....
}

Hope this helps.

Arun Gohil
  • 61
  • 4
  • I tried your way but I get an error: @Component.Builder is missing setters for required modules or components. Should I add something to my AppComponent? I do not have components for my Activities. – DimKe Oct 14 '19 at 12:41
  • try this link [link](https://stackoverflow.com/questions/44083243/dagger-2-component-builder-is-missing-setters-for-required-modules-or-componen). It has the same error you are getting. – Arun Gohil Oct 14 '19 at 13:15