Dagger 2 injecting Android Application Context
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
}
Comments
-
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 over 8 yearsCan you explain where you are replacing MainActivityModule here?
-
EpicPandaForce over 8 years@IgorGanapolsky I'm not sure why I had it with AppContext. It works essentially the same way, though.
-
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 almost 7 yearsFor dialog I'd just go with
this
-
eC Droid almost 7 years@EpicPandaForce can you please have a look at this question stackoverflow.com/q/45192878/5214893
-
b4da almost 6 years@EpicPandaForce Why is scope not necessary for parameters stored within the module?
-
EpicPandaForce almost 6 yearsBecause 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 over 4 yearsPlease see this question: stackoverflow.com/questions/59509103/…
-
Admin over 4 yearsI just answered!
-
Akbolat SSS over 4 yearsmost useful in 2020
-
Nicolás Arias almost 4 yearsThis is actually the best solution if Android injection is used
-
Johan Lund over 3 yearsvoid inject(MainActivity mainActivity); what is the use of this line???
-
EpicPandaForce over 3 yearsThe ability to field-inject
MainActivity
@JohanLund