This is a question I have in my project: https://github.com/vestrel00/android-dagger-butterknife-mvp/issues/42. I currently have a working solution: https://github.com/vestrel00/android-dagger-butterknife-mvp/pull/43. However, I'm hoping that future versions of Dagger may make it simpler (not that it isn't simple already).
Currently, Dagger 2.11 does not allow the Application instance to be implicitly provided using the same method used to provide Activity and Fragment instances;
abstract class AppModule {
@Binds
@Singleton
abstract Application application(App app);
}
The above gives the following compile-time error:
error: [dagger.android.AndroidInjector.inject(T)] com.vestrel00.daggerbutterknifemvp.App cannot be provided without an @Inject constructor or from an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
The current solution (and what I consider a workaround) is to store the Application instance in a module so that it may be provided;
@Module
class ApplicationProviderModule {
private final Application application;
ApplicationProviderModule(Application application) {
this.application = application;
}
@Provides
@Singleton
Application application() {
return application;
}
}
@Module(includes = {
AndroidInjectionModule.class,
ApplicationProviderModule.class
})
abstract class AppModule {
...
}
public class App extends Application implements HasActivityInjector {
...
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.applicationProviderModule(new ApplicationProviderModule(this))
.build()
.inject(this);
}
...
}
You can use @BindsInstance on a component builder.
On Fri, Aug 4, 2017, 9:58 AM vandolf estrellado notifications@github.com
wrote:
This is a question I have in my project:
vestrel00/android-dagger-butterknife-mvp#42
https://github.com/vestrel00/android-dagger-butterknife-mvp/issues/42.
I currently have a working solution:
vestrel00/android-dagger-butterknife-mvp#43
https://github.com/vestrel00/android-dagger-butterknife-mvp/pull/43.
However, I'm hoping that future versions of Dagger may make it simpler (not
that it isn't simple already).Currently, Dagger 2.11 does not allow the Application instance to be
implicitly provided using the same method used to provide Activity and
Fragment instances;abstract class AppModule {
@Binds
@Singleton
abstract Application application(App app);
}The above gives the following compile-time error:
error: [dagger.android.AndroidInjector.inject(T)] com.vestrel00.daggerbutterknifemvp.App cannot be provided without an @Inject constructor or from an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
The current solution (and what I consider a workaround) is to store the
Application instance in a module so that it may be provided;@Moduleclass ApplicationProviderModule {
private final Application application; ApplicationProviderModule(Application application) { this.application = application; } @Provides @Singleton Application application() { return application; }}
@Module(includes = {
AndroidInjectionModule.class,
ApplicationProviderModule.class
})abstract class AppModule {
...
}
public class App extends Application implements HasActivityInjector {
...
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.applicationProviderModule(new ApplicationProviderModule(this))
.build()
.inject(this);
}
...
}—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/google/dagger/issues/832, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEESXeGbBUWLno9x7GoL3Gl7hpRnuAks5sUyNugaJpZM4OtwEJ
.
@JakeWharton Of course! How did I not see that! I tried using @BindInstance but was using it incorrectly. The following works:
@Singleton
@Component(modules = AppModule.class)
interface AppComponent {
void inject(App app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
I saw a better solution and one that is more inline with Dagger 2.11. It makes use of AndroidInjector and AndroidInjector.Builder instead of manually declaring the inject(App app) and @BindsInstance methods.
@Singleton
@Component(modules = AppModule.class)
interface AppComponent extends AndroidInjector<App> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {
}
}
@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {
@Binds
@Singleton
// Singleton annotation isn't necessary (in this case since Application instance is unique)
// but is here for convention.
abstract Application application(App app);
...
}
public class App extends Application implements HasActivityInjector {
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().create(this).inject(this);
}
...
}
You should remove @Singleton. It creates a wasteful double-checked box
around the provider instead of being able to use the App instance directly.
On Sun, Aug 6, 2017, 8:13 AM vandolf estrellado notifications@github.com
wrote:
This is what I was looking for. It uses the AndroidInjector and
AndroidInjector.Builder instead of manually declaring the inject(App app)
and @BindsInstance methods.@Singleton
@Component(modules = AppModule.class)
interface AppComponent extends AndroidInjector{ @Component.Builder abstract class Builder extends AndroidInjector.Builder<App> { }}
@Module(includes = AndroidInjectionModule.class)
abstract class AppModule {@Binds @Singleton // Singleton annotation isn't necessary (in this case since Application instance is unique) // but is here for convention. abstract Application application(App app);...
}—
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/google/dagger/issues/832#issuecomment-320503242, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAEEEWSDz2xWahp_89NRb8c1dpvl362Uks5sVa3_gaJpZM4OtwEJ
.
Thanks for that info! I sometimes choose readability over performance (of auto-generated code) given its pros and cons. I'll play the devils advocate here and just update the comment to:
@Binds
@Singleton
/*
* Singleton annotation isn't necessary since Application instance is unique but is here for
* convention. In general, providing Activity, Fragment, BroadcastReceiver, etc does not require
* them to be scoped since they are the components being injected and their instance is unique.
*
* However, having a scope annotation makes the module easier to read. We wouldn't have to look
* at what is being provided in order to see understand scope.
*/
abstract Application application(App app);
Just my preference. Other people could totally disagree =)
Thank you so much vestrl00!! You've save me further hours of frustration as I try to wrap my head around the latest Dagger. My Room DB implementation was crashing because it couldn't find the application context from the injected application.
You saved my day, thank you so much! I was also struggling with Room implementation.
For me, this representation looks better:
App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder()
.create(this)
}
}
@Singleton
@Component(
modules = arrayOf(
AppModule::class,
//...
)
)
interface AppComponent : AndroidInjector<App> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<App>() {
}
}
md5-ff337754e39be4b1ca527217cc10fb56
@Module(includes = arrayOf(AndroidSupportInjectionModule::class, ...))
abstract class AppModule {
@Singleton
@Binds
@AppContext
abstract fun provideContext(app: App): Context
}
md5-ff337754e39be4b1ca527217cc10fb56
@Qualifier
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class AppContext
and finally you can use it in a way: @AppContext context : Context
For me, this representation looks better:
App : DaggerApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return DaggerAppComponent.builder() .create(this) } }@Singleton @Component( modules = arrayOf( AppModule::class, //... ) ) interface AppComponent : AndroidInjector<App> { @Component.Builder abstract class Builder : AndroidInjector.Builder<App>() { } }@Module(includes = arrayOf(AndroidSupportInjectionModule::class, ...)) abstract class AppModule { @Singleton @Binds @AppContext abstract fun provideContext(app: App): Context }@Qualifier @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) annotation class AppContextand finally you can use it in a way:
@AppContext context : Context
Same here! Only with a minor diff (update):
@Component.Factory
abstract class Builder : AndroidInjector.Factory<App>
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerApplicationComponent.factory().create(this)
}
Do you guys have any sample? @LeoDroidCoder and @GuilhE I implemented your way to provide application context everywhere but it seems like provideContext() is not getting from the generated classes as a result @AppContext lateinit var context: Context is remained uninitialized. Can you help me with a sample or can you help me in how to get the application context whereever I need? Thanks.
Do you guys have any sample? @LeoDroidCoder and @GuilhE I implemented your way to provide application context everywhere but it seems like
provideContext()is not getting from the generated classes as a result@AppContext lateinit var context: Contextis remained uninitialized. Can you help me with a sample or can you help me in how to get the application context whereever I need? Thanks.
I only use @AppContext when providing something in my modules that needs a context. What other cases do you need @AppContext?
Do you guys have any sample? @LeoDroidCoder and @GuilhE I implemented your way to provide application context everywhere but it seems like
provideContext()is not getting from the generated classes as a result@AppContext lateinit var context: Contextis remained uninitialized. Can you help me with a sample or can you help me in how to get the application context whereever I need? Thanks.I only use
@AppContextwhen providing something in my modules that needs a context. What other cases do you need@AppContext?
Component
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent : AndroidInjector<App> {
@Component.Factory abstract class Builder : AndroidInjector.Factory<App>
}
Module
@Module(includes = [AndroidInjectionModule::class])
abstract class AppModule {
@Singleton
@Binds
@AppContext abstract fun providesApplication(app: Application): Application
}
App
`class App: DaggerApplication() {
override fun onCreate() {
super.onCreate()
DaggerAppComponent.factory().create(this).inject(this)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.factory().create(this)
}
}`
Use
`class HomeViewModel : ViewModel() {
@AppContext
lateinit var application: Application
// TODO: Implement the ViewModel
fun displayToast() {
// Getting error "application not initialized" here
Toast.makeText(application.applicationContext, "DI Worked", Toast.LENGTH_LONG).show()
}
}`
You shouldn't use any Context reference inside your ViewModel. If you want to send a Toast send it in your Activity. There are a lot of ways to do so one, for instance, is to have your BaseActivity observing a LiveData that posts a value to be shown in a Toast. Your ViewModel justs holds an intance for that LiveData.
You shouldn't use any
Contextreference inside yourViewModel. If you want to send aToastsend it in yourActivity. There are a lot of ways to do so one, for instance, is to have yourBaseActivityobserving aLiveDatathat posts a value to be shown in a Toast. Your ViewModel justs holds an intance for thatLiveData.
Actually I don't need application context inside ViewModel, I need Database instance inside ViewModel and for that I will be needing context similarly for shared preferences and other things also.
Again, you never use any type of context inside your ViewModel. If you need a DataBase instance provide that instance inside a Module:
@Component(modules = [AppModule::class, ...])
@Singleton
interface AppComponent : AndroidInjector<YourApp> {
@Component.Factory
interface Factory : AndroidInjector.Factory<YourApp> {
override fun create(@BindsInstance instance: YourApp): AppComponent
}
}
@Module(
includes = [
AppProviders::class, ...
]
)
abstract class AppModule {
@Binds
@AppContext
abstract fun application(app: YourApp): Context
}
@Module
object AppProviders {
@Provides
@Singleton
fun provideSampleDatabase(context: YourApp): YourDatabase {
return Room.databaseBuilder(context, YourDatabase::class.java, "database").build()
}
...
}
Then Inject this database instance inside your Repository and Inject that Repository in your ViewModel and you're done.
Again, you never use any type of context inside your
ViewModel. If you need a DataBase instance provide that instance inside aModule:@Component(modules = [AppModule::class, ...]) @Singleton interface AppComponent : AndroidInjector<YourApp> { @Component.Factory interface Factory : AndroidInjector.Factory<YourApp> { override fun create(@BindsInstance instance: YourApp): AppComponent } }@Module( includes = [ AppProviders::class, ... ] ) abstract class AppModule { @Binds @AppContext abstract fun application(app: YourApp): Context }@Module object AppProviders { @Provides @Singleton fun provideSampleDatabase(context: YourApp): YourDatabase { return Room.databaseBuilder(context, YourDatabase::class.java, "database").build() } ... }Then
Injectthis database instance inside yourRepositoryandInjectthatRepositoryin yourViewModeland you're done.
Should I have to create a subcomponent for Injecting this?
If you follow my example and replace with your own classes will do.
I use the AppProviders module to provide instances that depend on Context.
For me, this representation looks better:
App : DaggerApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return DaggerAppComponent.builder() .create(this) } }@Singleton @Component( modules = arrayOf( AppModule::class, //... ) ) interface AppComponent : AndroidInjector<App> { @Component.Builder abstract class Builder : AndroidInjector.Builder<App>() { } }@Module(includes = arrayOf(AndroidSupportInjectionModule::class, ...)) abstract class AppModule { @Singleton @Binds @AppContext abstract fun provideContext(app: App): Context }@Qualifier @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) annotation class AppContextand finally you can use it in a way:
@AppContext context : ContextSame here! Only with a minor diff (update):
@Component.Factory abstract class Builder : AndroidInjector.Factory<App>override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return DaggerApplicationComponent.factory().create(this) }
How i can make inject(this) this in my fragment for example?
Again, you never use any type of context inside your
ViewModel. If you need a DataBase instance provide that instance inside aModule:@Component(modules = [AppModule::class, ...]) @Singleton interface AppComponent : AndroidInjector<YourApp> { @Component.Factory interface Factory : AndroidInjector.Factory<YourApp> { override fun create(@BindsInstance instance: YourApp): AppComponent } }@Module( includes = [ AppProviders::class, ... ] ) abstract class AppModule { @Binds @AppContext abstract fun application(app: YourApp): Context }@Module object AppProviders { @Provides @Singleton fun provideSampleDatabase(context: YourApp): YourDatabase { return Room.databaseBuilder(context, YourDatabase::class.java, "database").build() } ... }Then
Injectthis database instance inside yourRepositoryandInjectthatRepositoryin yourViewModeland you're done.
How can I inject this database anywhere?
No you can't, you must always inject database on your Repo that is best practice
Most helpful comment
Thanks for that info! I sometimes choose readability over performance (of auto-generated code) given its pros and cons. I'll play the devils advocate here and just update the comment to:
Just my preference. Other people could totally disagree =)