Hi i am new to dagger 2 and trying to inject an instance of sharedPreference inside my MyActivity class below:
class MyApplication : Application() {
companion object {
@JvmStatic lateinit var applicationComponent : ApplicationComponent
}
override fun onCreate() {
super.onCreate()
applicationComponent = DaggerApplicationComponent.builder().androidModule(AndroidModule(this)).build()
}
}
Here is the component and modules
@Singleton
@Component(modules = arrayOf(AndroidModule::class))
interface ApplicationComponent {
fun inject(mainActivity: MainActivity)
}
@Module
class AndroidModule (private val application: Application){
@Provides
@Singleton
fun provideApplicationContext() : Context = application
@Provides
@Singleton
fun provideSharedPreference() : SharedPreferences = application.getSharedPreferences("shared pref", Context.MODE_PRIVATE)
}
class MainActivity: Activity{
@Inject
internal lateinit var sharedPreference: SharedPreferences
@Inject
internal lateinit var MainScreenPresenter: MainScreenContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_Screen)
MyApplication.applicationComponent.inject(this)
sharedPreference.toString()
initiateViews()
}
}
I get the error below:
Error:(7, 1) error: android.content.SharedPreferences cannot be provided without an @Provides- or @Produces-annotated method.
You have done it a little bit incorrect. First of all now there is dagger-android that helps with the problem of principle that solves the problem that components (such as Activities) should not know about how the injection happens.
you can read it here: https://medium.com/@iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
Just to be sure that dagger dependencies are in the Android project:
android {
kapt {
generateStubs = true
}
}
compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile "com.google.dagger:dagger-android-support:2.13"
kapt "com.google.dagger:dagger-compiler:2.13"
kapt "com.google.dagger:dagger-android-processor:2.13"
Your mistake is that you didn't tell to your graph that you want to make injections into the MainActivity. In the best way you should create Subcomponent
for MainActivity, connect it with another Module for MainActivity that have injections that you want to inject into the MainActivity, set in your AppComponent the connection with Subcomponent and only than in MainAcitivy onCreate() method inject your dependency graph. But with dagger-android everything is easier.
Here is the code:
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBindingModule::class,
AppModule::class
])
interface AppComponent : AndroidInjector<DaggerApplication> {
fun inject(application: MyApplication)
override fun inject(instance: DaggerApplication)
@Component.Builder
interface Builder {
@BindsInstance fun application(application: MyApplication): Builder
fun build(): AppComponent
}
}
AndroidSupportInjectionModule.class : This goes from the dagger.android.support library. And it provides Android components (Activities/Fragments/Services/BroadcastReceiver/ContentProvider) with our module.
@Component.Builder in dagger2.10 provides us better way to create a builder of DaggerAppComponent.
@BindsInstance in the Builder will automatically create an instance of MyApplication
so in AppModule
we don't need to instantiate with MyApplication. It is already a dependency in the graph.
ActivityBindingModule.clas is our. I will tell about it later.
You can read more about this part here: https://proandroiddev.com/dagger-2-component-builder-1f2b91237856
Next is AppModule.class :
@Module
abstract class AppModule{
@Binds
abstract fun provideContext(application: MyApplication) : Context
@Module
companion object {
@JvmStatic
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences =
context.getSharedPreferences("SharedPreferences", Context.MODE_PRIVATE)
}
}
@Binds annotation replaces @Provides
annotation and it just returns the value in the function parameter. As you see an instance of MyApplication is already in the graph and there is no need to inject MyApplication in the AppModule constructor.
NOTE: function with @Binds annotation should be abstract, and if there are function with @Provides annotation they should be static
.
The solution in Kotlin for static funcitons you can find here: https://github.com/google/dagger/issues/900
ActivityBindingModule.class:
@Module
abstract class ActivityBindingModule {
@ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun bindMainActivity(): MainActivity
}
With the ActivityBindingModule class we just create separate Module that will create Subcomponents for Android components for us.
More about ContributesAndroidInjector and Binds you can read here: https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f
MainActivityModule.class:
@Module
abstract class MainActivityModule {
@Binds
internal abstract fun provideMainActivity(activity: MainActivity): MainActivity
}
MyApplication.class:
class MyApplication: DaggerApplication(){
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
val appComponent = DaggerAppComponent.builder()
.application(this)
.build()
appComponent.inject(this)
return appComponent
}
}
Do not forget insert Application in the Mainfest file.
<application
android:name=".MyApplication"
...
>
...
</application>
For your Application class you need to implement DaggerApplication that implements HasActivityInjector/HasFragmentInjector/etc as well as call AndroidInjection.inject().
About this you can read more here : https://google.github.io/dagger/android.html
MainActivity.class:
class MainActivity : DaggerAppCompatActivity() {
@Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("TAAAAG", sharedPreferences.toString())
}
}
As you can see MainActivity
now does not know how SharedPreferences are injected. Actually there is AndroidInjection.inject(this);
in the DaggerAppCompatActivity. If you don't extend you class from this, than you need to specify it in onCreate method by yourself, otherwise no injections will be done.
EDIT: You can check the code from GitHub: https://github.com/Belka1000867/Dagger2Kotlin
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With