The following is the code
Custom Annotation classes:
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class CtxMain
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class MyScope
Component:
@MyScope
@Component(modules = [ToastMakerModule::class,ActionsModule::class])
interface ActionsComponent {
fun injectIntoMain(activity: MainActivity)
}
Modules (Seperate File are made for each module):
@Module
class ActionsModule(private var context: Context) {
@Provides
@CtxMain
fun providesContext():Context{
return context
}
@Provides
fun providesSharedPrefs(@CtxMain context: Context): SharedPreferences{
return context.getSharedPreferences("PreferencesFile",Context.MODE_PRIVATE)
}
}
@Module
class ToastMakerModule {
@Provides
fun provideToastMaker(@CtxMain context: Context): ToastMaker{
return ToastMaker(context)
}
}
Models:
@MyScope
class ToastMaker @Inject constructor(@CtxMain private val context:Context) {
fun showToast(){
Toast.makeText(context,"This is a Toast",Toast.LENGTH_SHORT).show()
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var toastMaker: ToastMaker
@Inject
lateinit var toastMaker2: ToastMaker
@Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerActionsComponent.builder()
.actionsModule(ActionsModule(this))
.build()
.injectIntoMain(this)
toastMaker.showToast()
}
}
Upon running this code the expexted ouptut was that due to @MyScope annotation toastMaker and toastMaker2 should point to the same object but they are not even tough the ToastMaker class has been annotated with @MyScope but still different obejcts are created for each (different hashcodes), but upon modifying the code (removing the provider for ToastMaker) i.e.
Modified ToastMakerModule (commented the provider)
@Module
class ToastMakerModule {
// @Provides
// fun provideToastMaker(@CtxMain context: Context): ToastMaker{
// Log.d("#D","Provider de ToastMaker")
// return ToastMaker(context)
// }
}
Now when the Field Injection is coming from the ToastMaker Class itself, both toastMaker and toastMaker2 point to the same obejct (same hashcode), What am i missing here? why is provider method not returning me the same instance even tough its marked with @MyScope. i know we can mark the provider method with @MyScope annotation to make provider method return the same instance but shouldnt this work because the ToastMaker class is annotated with @MyScope. when provider method is removed its working fine, why?
Because you've offered Dagger a @Provides
method, it will use that and completely ignore the @Inject
-annotated constructor and any scopes you list on the class.
As Dagger maintainer Brad Corso mentions in google/dagger#3361 "Singletons are initilized several times when injected into application" (emphasis mine):
However, even if your actual
SubsManager
class does have an@Inject
-annotated constructor you should still verify it is not provided via a module since bindings provided via a module can implicitly override bindings provided via an@Inject
-annotated constructor.
To understand this behavior, it is helpful to think of the @Inject
-annotated constructor and class scope annotations as a single definition, just like the @Provides
method and its scope annotations are a single definition. Rather than trying to merge the two definitions (using the scope from the class-annotation definition and the implementation from the @Provides method definition), Dagger just observes the scopeless @Provides method definition and entirely overrides the class-annotation definition.
Dagger behaves as if you are deliberately trying to override the scope on ToastMaker to make it scopeless--which is a particularly reasonable assumption because you can customize your set of modules per Dagger component but you can only define your potentially-reusable ToastMaker class exactly once. If you need to override the class, including scopes defined directly on the class, then a @Provides
method on a module is your natural choice and you absolutely wouldn't want the scope definition on the class to be merged into your module override.
To fix this problem, you'll need to either remove the @Provides
method (such that the @Inject
constructor and scope remain together), or add the appropriate @MyScope
annotation onto the @Provides
method.