Is there a way to add ContributesAndroidInjector for activity in a Subcomponent? I am trying to create a scope that works across a few activities.
What I have is this:
AppComponent
@Component(modules = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class
])
interface AppComponent: AndroidInjector<DaggerApplication> {
override fun inject(instance: DaggerApplication)
fun chatComponent(): ChatComponent.Builder
fun inject(needle: Needle)
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): AppComponent.Builder
fun build(): AppComponent
}
}
ChatScope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ChatScope
ChatComponent
@Subcomponent(modules = [ChatBindingModule::class])
@ChatScope
interface ChatComponent {
@Subcomponent.Builder
interface Builder {
fun chatModule(chatModule: ChatModule): Builder
fun build(): ChatComponent
}
}
ChatBindingModule
@Module
abstract class ChatBindingModule {
@ActivityScoped
@ContributesAndroidInjector(modules = [])
abstract fun chatListActivity(): ChatListActivity
@ActivityScoped
@ContributesAndroidInjector(modules = [])
abstract fun chatDetailActivity(): ChatDetailActivity
}
With this code dagger fails to find injector for both activities. I guess I do know why that is happening but no idea how to solve this problem
You'd need to provide custom code in your Application instance that would know how to access ChatComponent. Something like this:
class MyApplication : DaggerApplication {
override fun activityInjector(): AndroidInjector<Activity> {
return object : AndroidInjector<Activity> {
override fun inject(activity: Activity) {
if (activity.isChatActivity()) {
component.chatComponent().activityInjector().inject(activity)
} else {
component.activityInjector().inject(activity)
}
}
}
}
component has access to chatComponent() but how do I get chatComponent().activityInjector() and component.activityInjector()?
You can add methods like:
@Component
interface RootComponent {
fun activityInjector(): DispatchingAndroidInjector<Activity>
fun ChatComponent chatComponent()
}
@Subcomponent
interface ChatComponent {
fun activityInjector(): DispatchingAndroidInjector<Activity>
}
Sorry for reviving this but FYI for anyone trying to implement this, ronshapiro's doesn't work if quite work if you subclass DaggerApplication since it actually returns a DispatchingAndroidInjector which is final instead of an AndroidInjector. Instead here's my application class:
class MyApplication : Application(), HasActivityInjector {
@Inject
lateinit var appDispatchingActivityInjector: DispatchingAndroidInjector<Activity>
private val appComponent: AppComponent by lazy {
DaggerAppComponent
.builder()
.application(this)
.build()
}
var userSessionComponent: UserSessionComponent? = null
override fun onCreate() {
super.onCreate()
appComponent.inject(this)
}
override fun beginUserSession() {
userSessionComponent = appComponent.userSessionComponent().build()
}
override fun endUserSession() {
userSessionComponent = null
}
override fun activityInjector(): AndroidInjector<Activity> {
return AndroidInjector {
// Try to inject using more tightly scoped components first.
userSessionComponent?.activityInjector()?.maybeInject(it) ?: false ||
appDispatchingActivityInjector.maybeInject(it)
}
}
}
An advantage of this approach is that you don't have to duplicate your @ContributesAndroidInjector activity bindings in activityInjector()
Disclaimer: Hack
Lets say I have an app component as mentioned below
@Component(
modules = [
AndroidSupportInjectionModule::class,
MainActivityModule::class]
)
interface AppComponent {
fun inject(application: MyApplication)
}
Then I have a sub component as mentioned below.
@Module
interface MainActivityModule {
@ContributesAndroidInjector(modules = [SecondActivityModule::class])
fun contributeMainActivityInjector(): MainActivity
}
Then I have a nested subcomponent as mentioned below.
@Module
interface SecondActivityModule {
@ContributesAndroidInjector
fun contributeMainActivityInjector(): SecondActivity
}
Invoking AndroidInjection.inject(this) from SecondActivity will throw an exception because AndroidInjection class try to use DispatchingAndroidInjector<Activity> injected in application class which does not have AndroidInjector.Factory for SecondActivity. But that factory is available in MainActivity DispatchingAndroidInjector<Activity>
So hack is every time an application class implements HasActivityInjector interface, it can store/update DispatchingAndroidInjector<Activity> in application class.
@dalvin your component hierarchy is faulty in the first place. Activities are independent of each other, and having the second activity injector inside the main activity doesn't work. You'll run into issues when android restores your state after a background app death. You should organize your components so that the activity android injectors are all at the application component level.
Most helpful comment
You'd need to provide custom code in your Application instance that would know how to access ChatComponent. Something like this: