How to inject Application instance in #Provides? - android

My AppModule crashes in compile time with error:
error: .App cannot be provided without an #Inject constructor or from an #Provides-annotated method.
public abstract .vcs.IGitHubApi getGitHubApi();
^
.App is injected at
.AppModule.provideOAuth2Interceptor(app)
.vcs.OAuth2Interceptor is injected at
.AppModule.provideOkHttpClient(…, oAuth2Interceptor)
okhttp3.OkHttpClient is injected at
.AppModule.provideRetrofit(httpClient, …)
retrofit2.Retrofit is injected at
.AppModule.provideGitHubApi(retrofit)
.vcs.IGitHubApi is provided at
.AppComponent.getGitHubApi()
Here is my AppModule class:
#Module
class AppModule {
// other providers
#Singleton
#Provides
fun provideOAuth2Interceptor(app: App): OAuth2Interceptor {
return OAuth2Interceptor(app)
}
}
AppComponent:
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
// other methods
fun inject(app: App)
#Component.Builder
interface Builder {
#BindsInstance
fun context(context: Context): Builder
fun build(): AppComponent
}
}
And my App class where I initialize AppComponent:
class App: Application() {
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.context(this)
.build()
.inject(this)
}
}
I get that Dagger can't find App to build provideOAuth2Interceptor but I don't know how to inject App in provider.
P.S. I am learning Dagger still.

In your AppComponent, you should bind the instance of your App class to make it a part of Dagger graph.
#Component.Builder
interface Builder {
#BindsInstance
fun context(context: Context): Builder
#BindsInstance
fun application(app: App): Builder
fun build(): AppComponent
}
and in your App class, provide the instance of App to the component while construction-
DaggerAppComponent.builder()
.context(this)
.application(this)
.build()
.inject(this)

Related

Cannot inject object provided in AppModule to Activity in a dynamic feature module with Dagger2

With the set up below i'm not be able to inject a Singleton object to a Activity inside a dynamic feature module. I can inject to a subComponent, MainActivity, but not to an Activity that's in dynamic feature module.
#Component(modules = [AppModule::class])
interface AppComponent {
fun inject(application: Application)
#Component.Factory
interface Factory {
fun create(#BindsInstance application: Application): AppComponent
}
// Types that can be retrieved from the graph
fun mainActivityComponentFactory(): MainActivitySubComponent.Factory
}
My AppModule
#Module(includes = [AppProviderModule::class])
abstract class AppModule {
#Binds
abstract fun bindContext(application: Application): Context
}
#Module
object AppProviderModule {
#Provides
#Singleton
fun provideSharedPreferences(application: Application): SharedPreferences {
return application.getSharedPreferences("PrefName", Context.MODE_PRIVATE)
}
}
Dynamic Feature Module GalleryComponent
#GalleryScope
#Component(
dependencies = [AppComponent::class],
modules = [GalleryModule::class])
interface GalleryComponent {
fun inject(galleryActivity: GalleryActivity)
}
And MyApplication
open class MyApplication : Application() {
// Instance of the AppComponent that will be used by all the Activities in the project
val appComponent: AppComponent by lazy {
initializeComponent()
}
open fun initializeComponent(): AppComponent {
// Creates an instance of AppComponent using its Factory constructor
// We pass the applicationContext that will be used as Application
return DaggerAppComponent.factory().create(this).apply {
inject(this#MyApplication)
}
}
}
Activity in dynamic feature module, when only inject GalleryViewer and DummyDependency is injected from GalleryModule it works fine
class GalleryActivity : AppCompatActivity() {
#Inject
lateinit var sharedPreferences: SharedPreferences
#Inject
lateinit var galleryViewer: GalleryViewer
#Inject
lateinit var dummyDependency: DummyDependency
override fun onCreate(savedInstanceState: Bundle?) {
DaggerGalleryComponent.builder()
.appComponent((application as MyApplication).appComponent)
.build()
.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)
}
When i try to inject SharedPreferences or any dependency that does not depend on any arguments like context or application from AppModule i get error
error: [Dagger/MissingBinding] android.content.SharedPreferences cannot be provided without an #Provides-annotated method.
The error here is not including provision method for dependencies in AppComponent and not building dynamic feature component properly.
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
/**
* 🔥🔥🔥 This method is required to get this object from a class that uses this component
* as dependent component
*/
fun provideSharedPreferences(): SharedPreferences
fun inject(application: Application)
#Component.Factory
interface Factory {
fun create(#BindsInstance application: Application): AppComponent
}
// Types that can be retrieved from the graph
fun mainActivityComponentFactory(): MainActivitySubComponent.Factory
}
In dynamic feature
#GalleryScope
#Component(
dependencies = [AppComponent::class],
modules = [GalleryModule::class])
interface GalleryComponent {
fun inject(galleryActivity: GalleryActivity)
// Alternative1 With Builder
#Component.Builder
interface Builder {
fun build(): GalleryComponent
#BindsInstance
fun application(application: Application): Builder
fun galleryModule(module: GalleryModule): Builder
fun appComponent(appComponent: AppComponent): Builder
}
// Alternative2 With Factory
#Component.Factory
interface Factory {
fun create(appComponent: AppComponent,
galleryModule: GalleryModule,
#BindsInstance application: Application): GalleryComponent
}
}
You must either use Builder or Factory, with hilt none of this might be necessary in the future, however it does not support dynamic feature yet, i rather factory pattern since they deprecated Builder pattern.
inside Activity onCreate initialize injection
private fun initInjections() {
// Alternative1 With Builder
DaggerGalleryComponent.builder()
.appComponent((application as MyApplication).appComponent)
.application(application)
.galleryModule(GalleryModule())
.build()
.inject(this)
// Alternative2 With Factory
DaggerGalleryComponent
.factory()
.create((application as MyApplication).appComponent, GalleryModule(), application)
.inject(this)
}
You should choose the same pattern used inside feature component.

Android dagger2 , some things not clear

I'm learning dagger2 with a module architecture. And I think, something is not clear to me, for example
in module utilites i have di package
class UtilsComponent
#Component(modules = [UtilsModule::class])
interface UtilsComponent {
fun getResourceProvider() : IResourceProvider
fun getNetworkProvider(): INetworkProvider
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): UtilsComponent.Builder
fun build(): UtilsComponent
}
}
#Module
abstract class UtilsModule {
#Binds
abstract fun bindContext(application: Application): Context
#Module
companion object {
#Provides
#JvmStatic
fun bindResourceProvider(context: Context): IResourceProvider {
return ResourceProvider(context = context)
}
#Provides
#JvmStatic
fun bindNetworkProvider(context: Context): INetworkProvider {
return NetworkProvider(context = context)
}
}
}
then in app package in AppComponent i included all modules
#Component(
dependencies = [UtilsComponent::class],
modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class,
MainModule::class // test module
]
)
#AppScope
interface AppComponent: AndroidInjector<App> {
// inject to ...
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun utilsComponent(utilsComponent: UtilsComponent): AppComponent.Builder
fun build(): AppComponent
}
}
In the app component, I have MainModule - this is my test module, so this module looks like this
#Module
class MainModule {
#Provides
fun getMainPresenter(networkProvider: NetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
}
when I run the app, I have an error
[Dagger/MissingBinding] com.example.utilities.di.UtilsModule cannot be
provided without an #Provides-annotated method. public abstract
interface AppComponent extends
dagger.android.AndroidInjector<com.example.testmoduleapp.App> {
^
com.example.utilities.di.UtilsModule is injected at
com.example.testmoduleapp.di.modules.MainModule.getMainPresenter(utilsModule)
com.example.testmoduleapp.ui.activities.main.MainPresenter is injected at
com.example.testmoduleapp.ui.activities.main.MainActivity.mainPresenter
com.example.testmoduleapp.ui.activities.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.example.testmoduleapp.di.AppComponent →
com.example.testmoduleapp.di.modules.ActivityBindingModule_MainActivity.MainActivitySubcomponent]
I understand that the error is because I have not a module witch return NetworkProvider object, but I can't understand how I can get this object from UtilsModule
also in App
#Inject
lateinit var androidInjector : DispatchingAndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = androidInjector
companion object{
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
initializeDagger()
}
private fun initializeDagger() {
appComponent = DaggerAppComponent
.builder()
.application(this)
.utilsComponent(provideUtilsComponent())
.build()
}
private fun provideUtilsComponent(): UtilsComponent {
return DaggerUtilsComponent
.builder()
.application(this)
.build()
}
In Dependencies Graph You already provide INetworkProvider so when you need NetworkProvider it means Dagger can not be resolved. Change to this, don't forget to change constructor MainPresenter to INetworkProvider
#Provides
fun getMainPresenter(networkProvider: INetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
but i cant understand how i can get this object from UtilsModule
For your question, in UtilComponent you already exposed getNetworkProvider() it means any Componenent dependencies to UtilsComponent can be get it.

How do i provide application instance in a module

I'm not able to build my app due to following error.
#Component.Builder is missing setters for required modules or components
I'm using
implementation "androidx.room:room-runtime:2.1.0"
kapt "androidx.room:room-compiler:2.1.0"
My apps RoomModul looks like this:
#Module
class RoomModule(application: Application) {
#Singleton
private var logDatabase : LogDatabase =
Room.databaseBuilder(application, LogDatabase::class.java, "log-db").build()
#Singleton
#Provides
fun providesLogDatabase() : LogDatabase {
return logDatabase
}
#Singleton
#Provides
fun providesLogDao() : LogDao {
return logDatabase.getLogDao()
}
#Singleton
#Provides
fun providesLogRepository(logDao: LogDao) : LogRepository {
return LogDataSource(logDao)
}
}
And my AppComponent looks like this:
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
MainActivityModule::class,
RoomModule::class
])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(logApplication: LogApplication)
}
So my RoomModule needs the application to provide the room database. But I keep getting the error.
I thought that #BindsInstance should provide the application instance to my modules. I also tried to remove the constructor from my RoomModule with no success. Please let me know if I can provide more information.
Try to use this
#Module
class RoomModule()
{
#Singleton
#Provides
private var logDatabase(application: Application) : LogDatabase =
Room.databaseBuilder(application, LogDatabase::class.java, "log-db").build()
----
}
And Remove this line ---> fun inject(logApplication: LogApplication)
from AppComponent

Android dagger cannot be provided without an Provides-annotated method

I decided to learn dagger dependency injection framework. After some tutorials I try to implement dagger into my project. However I got this error
com\assigment\di\component\AppComponent.java:11: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> cannot be provided without an #Provides-annotated method.
public abstract void inject(#org.jetbrains.annotations.NotNull()
^
java.util.Map<java.lang.Class<? extends android.app.Activity>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.app.Activity>>> is injected at
dagger.android.DispatchingAndroidInjector.<init>(injectorFactories)
dagger.android.DispatchingAndroidInjector<android.app.Activity> is injected at
assigment.com.assigment.App.activityInjector
assigment.com.assigment.App is injected at
assigment.com.assigment.di.component.AppComponent.inject(assigment.com.assigment.App)
I try to solve this for 2 days but with no luck. So here's how I set up my project
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent
.builder()
.appModule(AppModule())
.build()
}
override fun activityInjector(): AndroidInjector<Activity> {
return activityInjector
}
}
Here's my app component
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
and here's my appModule class
AppModule
#Module
class AppModule {
private val url = "http://test.lt/v1/"
#Provides
#Singleton
fun provideApplication(app: Application): Context = app
}
So what I'm missing with this implementation?
Add AndroidInjectionModule.class and ActivityBuilder.class to your AppComponent
#Singleton
#Component(modules = [AndroidInjectionModule::class, AppModule::class, ActivityBuilder::class ])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
You have two problems.
To get rid of the compiler error add AndroidInjectionModule::class to Component modules:
#Singleton
#Component(modules = [AndroidInjectionModule::class, AppModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
With #Component.Builder annotated interface you define the builder interface with an annotated #BindsInstance method application() (note that there is not an appModule method declared in the Builder).
With such declaration you can build your component using application(this):
appComponent = DaggerAppComponent
.builder()
.application(this)
.build()
In this way this application instance is bound inside the component.
Just as a side note: Binding Instances are documented here, but personally I found the explanation quite hard to grasp for someone learning dagger, like me.
Detail answer with explanation
Component - >
Component is a graph. Component will provide injected instances by using modules.
#Component(
modules = [
AndroidInjectionModule::class, //We didn’t create this. It is an internal class in Dagger 2.10. Provides our activities and fragments with given module
ActivityModule::class,
]
)
#Singleton
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun addContext(#BindsInstance context: Context): Builder
fun build(): AppComponent
}
}
We created ActivityModule module. This is a given module to dagger. We map all our activities here. And Dagger know our activities in compile time. In our app we have MainActivity. So we map it here.
#Module
abstract class ActivityModule {
#ContributesAndroidInjector
public abstract MainActivity bindMainActivity();
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application
android:name=".App"
App.kt
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().addContext(this).build()
}
}
And Make sure to extend your activity with DaggerAppCompatActivity that will auto inject before onCreate

Dynamically add test modules in appComponent dagger 2?

Hi is it possible to add test modules in my AppComponent?
Below is my real representation of my appComponent
#Singleton
#Component(modules = arrayOf(MainModule::class,
AnalyticsModule::class,
MainAndroidBinding::class,
AccountAndroidBinding::class,
AndroidSupportInjectionModule::class,
HomeAndroidBinding::class,
NetworkModule::class))
interface ApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: Application): Builder
fun build(): ApplicationComponent
}
}
I could just add the test modules directly to the testAppComponent like this but it doesnt offer me much flexibility to dynamically add different testModules.
#Singleton
#Component(modules = [
(MainModuleTest::class),
(TestMainAndroidBindingTest::class),
(AccountAndroidBindingTest::class),
(AnalyticsModuleTest::class),
(AndroidSupportInjectionModule::class),
(NetworkModuleTest::class)])
interface TestAppComponent : ApplicationComponent {
fun inject(launchActivityTest: LaunchActivityTest)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: Application): Builder
fun build(): TestAppComponent
}
}
Here is my MyApplication class
class MyApplication : DaggerApplication() {
companion object {
private lateinit var application: MyApplication
fun get(): MyApplication {
return application
}
}
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
lateinit var applicationComponent: ApplicationComponent
override fun onCreate() {
super.onCreate()
application = this
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
applicationComponent = DaggerApplicationComponent.builder()
.application(this)
.build()
applicationComponent.inject(this)
return applicationComponent
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
On the LaunchActivityTest this is how i set it up to use this testApp component
#Before
fun setUp() {
val app = InstrumentationRegistry.getTargetContext().applicationContext as MyApplication
val testAppComponent = DaggerTestAppComponent.builder().application(app).build()
app.applicationComponent = testAppComponent
testAppComponent.inject(this)
}
I was following this guide until i stumbled o the point where my DaggerTestAppComponent doesnt expose the modules for me to dynamically add myself due to the fact that my AppComponent class extends AndroidInjector which automatically adds the modules for you
https://proandroiddev.com/writing-espresso-instrumentation-tests-with-dagger2-kotlin-d30f12c4769b
The above dynamically adds its modules like this:
testAppComponent = DaggerTestAppComponent.builder()
.appModule(AppModule(app))
.apiModule(TestApiModule())
.prefModule(TestPrefModule())
.build()
I cant do that in my case unless i redo my AppComponent so that it doesnt extend AndroidInjector. If i do that then in my real impl code i have to manually set the modules.
Is there any other way?
You should add a function to your component builder and use "BindsInstance".
Example component
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationTestModule.class,
ActivityBuilder.class})
public interface TestExampleComponent extends AndroidInjector<DaggerApplication> {
void inject(TestApplication app);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
TestExampleComponent.Builder application(DaggerApplication application);
Builder applicationModule(ApplicationTestModule appTestModule);
TestExampleComponent build();
}
}
In this component, I added applicationModule function with using "BindsInstance" and I can pass ApplicationTestModule.
Then you can add different test modules.
TestApplicationComponent appComponent = DaggerTestAppComponent.builder().application(this).
applicationModule(appTestModule).build();
appComponent.inject(this);

Categories

Resources