Dagger 2 injecting Android Application Context

50,154

Solution 1

Was not correctly building the Application component, needed to pass in the Application. This Dagger 2 example perfectly shows how to do this: https://github.com/google/dagger/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple

Update:
Working link: https://github.com/yongjhih/dagger2-sample/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple

Solution 2

@Module
public class MainActivityModule {    
    private final Context context;

    public MainActivityModule (Context context) {
        this.context = context;
    }

    @Provides //scope is not necessary for parameters stored within the module
    public Context context() {
        return context;
    }
}

@Component(modules={MainActivityModule.class})
@Singleton
public interface MainActivityComponent {
    Context context();

    void inject(MainActivity mainActivity);
}

And then

MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
    .mainActivityModule(new MainActivityModule(MainActivity.this))
    .build();

Solution 3

It took me a while to find a proper solution, so thought it might save some time for others, as far as I could gather this is the preferred solution with the current Dagger version (2.22.1).

In the following example I need the Application's Context to create a RoomDatabase (happens in StoreModule).

Please if you see any errors or mistakes let me know so I'll learn as well :)

Component:

// We only need to scope with @Singleton because in StoreModule we use @Singleton
// you should use the scope you actually need
// read more here https://google.github.io/dagger/api/latest/dagger/Component.html
@Singleton
@Component(modules = { AndroidInjectionModule.class, AppModule.class, StoreModule.class })
public interface AwareAppComponent extends AndroidInjector<App> {

    // This tells Dagger to create a factory which allows passing 
    // in the App (see usage in App implementation below)
    @Component.Factory
    interface Factory extends AndroidInjector.Factory<App> {
    }
}

AppModule:

@Module
public abstract class AppModule {
    // This tell Dagger to use App instance when required to inject Application
    // see more details here: https://google.github.io/dagger/api/2.22.1/dagger/Binds.html
    @Binds
    abstract Application application(App app);
}

StoreModule:

@Module
public class StoreModule {
    private static final String DB_NAME = "aware_db";

    // App will be injected here by Dagger
    // Dagger knows that App instance will fit here based on the @Binds in the AppModule    
    @Singleton
    @Provides
    public AppDatabase provideAppDatabase(Application awareApp) {
        return Room
                .databaseBuilder(awareApp.getApplicationContext(), AppDatabase.class, DB_NAME)
                .build();
    }
}

App:

public class App extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();

        // Using the generated factory we can pass the App to the create(...) method
        DaggerAwareAppComponent.factory().create(this).inject(this);
    }

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

Solution 4

I have read this article and it was very helpful.

https://medium.com/tompee/android-dependency-injection-using-dagger-2-530aa21961b4

Sample code.

Update: I removed from AppComponent.kt these lines because are not necessaries

fun context(): Context
fun applicationContext(): Application

AppComponent.kt

   @Singleton
    @Component(
        modules = [
            NetworkModule::class,
            AppModule::class
        ]
    )
    interface AppComponent {
        fun inject(viewModel: LoginUserViewModel)
    }

AppModule.kt

@Module
class AppModule(private val application: Application) {

    @Provides
    @Singleton
    fun providesApplication(): Application = application

    @Provides
    @Singleton
    fun providesApplicationContext(): Context = application

    @Singleton
    @Provides
    fun providesNetworkConnectivityHelper(): NetworkConnectivityHelper{
        return NetworkConnectivityHelper(application.applicationContext)
    }
}

NetworkConnectivityHelper.kt

And only added @Inject constructor to pass the Context

class NetworkConnectivityHelper @Inject constructor(context: Context) {

    private val connectivityManager =
        context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

    @Suppress("DEPRECATION")
    fun isNetworkAvailable(): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val nc = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)

            nc != null
                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        }

        val networkInfo = connectivityManager.activeNetworkInfo
        return networkInfo != null && networkInfo.isConnected
    }
}

App class.kt

class App : Application() {

    lateinit var appComponent: AppComponent

    override fun onCreate() {
        super.onCreate()
        this.appComponent = this.initDagger()
    }

    private fun initDagger() = DaggerAppComponent.builder()
        .appModule(AppModule(this))
        .build()
}

Finally in my Activity I injected my helper

 @Inject lateinit var networkConnectivity: NetworkConnectivityHelper

And YEI! it works for me.

Solution 5

probably we could inject the context as shown below:

the application component

@Component(
    modules = [
        (ApplicationModule::class),
        (AndroidSupportInjectionModule::class),
        (UiBindingModule::class)
    ]
)
interface ApplicationComponent : AndroidInjector<AndroidApplication> {

    override fun inject(application: AndroidApplication)

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: AndroidApplication): Builder

        @BindsInstance
        fun context(context: Context): Builder

        fun build(): ApplicationComponent
    }
}

the custom application extending dagger application

class AndroidApplication : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerApplicationComponent.builder().application(this).context(this).build()
    }
}

Example ApplicationModule

@Module
abstract class ApplicationModule {

    /**
     * Binds a background thread executor, which executes threads from a thread pool
     * @param jobExecutor
     * @return
     */
    @Binds
    internal abstract fun provideThreadExecutor(jobExecutor: JobExecutor): ThreadExecutor

    /**
     * Binds main ui looper thread
     * @param uiThread
     * @return
     */
    @Binds
    internal abstract fun providePostExecutionThread(uiThread: UIThread): PostExecutionThread

}

Example UI BindingModule

@Module
abstract class UiBindingModule {

    @ContributesAndroidInjector(modules = [(MainActivityModule::class)])
    internal abstract fun mainActivity(): MainActivity

    @ContributesAndroidInjector(modules = [(MapFragmentModule::class)])
    internal abstract fun mapFragment(): MapFragment

}
Share:
50,154
user3521637
Author by

user3521637

Tech: Android, Java, C++

Updated on July 12, 2022

Comments

  • user3521637
    user3521637 almost 2 years

    I am using Dagger 2 and have it working however I now need access to the Android Application Context.

    Its not clear to me how to inject and get access to the context. I have tried to do this as follows:

    @Module
    public class MainActivityModule {    
        private final Context context;
        
        MainActivityModule(Context context) {
            this.context = context;
        }
    
        @Provides @Singleton
        Context provideContext() {
            return context;
        }
    }
    

    However this results in the following exception:

    java.lang.RuntimeException: Unable to create application : java.lang.IllegalStateException: mainActivityModule must be set

    If I inspect the Dagger generated code this exception is raised here:

    public Graph build() {  
        if (mainActivityModule == null) {
            throw new IllegalStateException("mainActivityModule must be set");
        }
        return new DaggerGraph(this);
    }
    

    I am not sure if this is the correct way to get Context injected - any help will be greatly appreciated.

  • IgorGanapolsky
    IgorGanapolsky over 8 years
    Can you explain where you are replacing MainActivityModule here?
  • EpicPandaForce
    EpicPandaForce over 8 years
    @IgorGanapolsky I'm not sure why I had it with AppContext. It works essentially the same way, though.
  • Dr.jacky
    Dr.jacky almost 7 years
    @EpicPandaForce In an Activity, should I use DI or just 'this'? For example to show a Dialog in an Activity
  • EpicPandaForce
    EpicPandaForce almost 7 years
    For dialog I'd just go with this
  • eC Droid
    eC Droid almost 7 years
    @EpicPandaForce can you please have a look at this question stackoverflow.com/q/45192878/5214893
  • b4da
    b4da almost 6 years
    @EpicPandaForce Why is scope not necessary for parameters stored within the module?
  • EpicPandaForce
    EpicPandaForce almost 6 years
    Because the scoped provider isn't needed for it to provide the same instance . It will only exist once as it is always provided from the field in the module
  • Malwinder Singh
    Malwinder Singh over 4 years
    Please see this question: stackoverflow.com/questions/59509103/…
  • Admin
    Admin over 4 years
    I just answered!
  • Akbolat SSS
    Akbolat SSS over 4 years
    most useful in 2020
  • Nicolás Arias
    Nicolás Arias almost 4 years
    This is actually the best solution if Android injection is used
  • Johan Lund
    Johan Lund over 3 years
    void inject(MainActivity mainActivity); what is the use of this line???
  • EpicPandaForce
    EpicPandaForce over 3 years
    The ability to field-inject MainActivity @JohanLund