I'm using Dagger 2 for dependency injection in my project
The problem is when I'm trying to inject a ViewPageAdapter inside the fragment class which depend on FragmentManager like bellow:
@Inject
lateinit var mainTripsFragmentAdapter: MainTripsFragmentAdapter
I get the following build error:
error: [dagger.android.AndroidInjector.inject(T)] android.support.v4.app.FragmentManager cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector { ^ android.support.v4.app.FragmentManager is injected at com.example.presenters.main.trips.MainTripsFragmentAdapter.(fragmentManager, …) com.example.presenters.main.trips.MainTripsFragmentAdapter is injected at com.example.presenters.main.trips.MainTripsFragment.mainTripsFragmentAdapter com.example.presenters.main.trips.MainTripsFragment is injected at com.example.presenters.main.MainActivity.tripsFragment com.example.presenters.main.MainActivity is injected at dagger.android.AndroidInjector.inject(arg0) A binding with matching key exists in component: com.example.di.FragmentBuilder_BindMainTripsFragment$app_debug.MainTripsFragmentSubcomponent
I've researched for 3 days and I've read many articles but I couldn't figure out where the problem is!
I'll post the related code, sorry if it's too long
The Fragment which needs PagerAdapter:
class MainTripsFragment @Inject constructor() : BaseFragment() {
@Inject
lateinit var mainTripsFragmentAdapter: MainTripsFragmentAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
pager.adapter = mainTripsFragmentAdapter
}
}
BaseFragment:
abstract class BaseFragment : DaggerFragment() {
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(getLayout(), container, false)
abstract fun getLayout(): Int
}
Here is the FragmentPagerAdapter:
class MainTripsFragmentAdapter @Inject constructor(
fragmentManager: FragmentManager,
private val mainCurrentTripsFragment: MainCurrentTripsFragment,
private val mainHistoryTripsFragment: MainHistoryTripsFragment
) : FragmentPagerAdapter(fragmentManager) {
override fun getItem(position: Int) = when (position) {
0 -> mainCurrentTripsFragment
1 -> mainHistoryTripsFragment
else -> mainCurrentTripsFragment
}
override fun getCount() = 2
}
And here is DI related classes:
AppComponent:
@Singleton
@Component(modules = [
AndroidInjectionModule::class,
ActivityBuilder::class,
FragmentBuilder::class
])
interface AppComponent : AndroidInjector<DaggerApplication> {
fun inject(app: App)
override fun inject(instance: DaggerApplication)
@Component.Builder
interface Builder {
@BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
FragmentBuilder:
@Module
abstract class FragmentBuilder {
@ContributesAndroidInjector
internal abstract fun bindMainHomeFragment(): MainHomeFragment
@Module
internal interface MainTripsFragmentModule {
@Binds
fun bindMainTripsFragment(fragment: MainTripsFragment): Fragment
}
@ContributesAndroidInjector(modules = [FragmentModule::class, MainTripsFragmentModule::class])
internal abstract fun bindMainTripsFragment(): MainTripsFragment
@ContributesAndroidInjector
internal abstract fun bindMainCurrentTripsFragment(): MainCurrentTripsFragment
@ContributesAndroidInjector
internal abstract fun bindMainHistoryTripsFragment(): MainHistoryTripsFragment
}
FragmentModule:
@Module
class FragmentModule {
@Provides
fun provideFragmentManager(fragment: Fragment) = fragment.childFragmentManager
}
ActivityBuilder:
@Module
abstract class ActivityBuilder {
@ContributesAndroidInjector
internal abstract fun contributeMainActivity(): MainActivity
}
MainActivity:
class MainActivity : BaseActivity() {
@Inject
lateinit var tripsFragment: MainTripsFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showFragment(tripsFragment)
}
private fun showFragment(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(R.id.layout_container, fragment)
.commit()
}
}
BaseActivity:
@SuppressLint("Registered")
abstract class BaseActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject
lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
override fun supportFragmentInjector() = fragmentInjector
}
Application Class:
class App : Application(), HasActivityInjector {
@Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.application(this)
.build()
.apply {
inject(this@App)
}
}
}
One point is that if I @Inject PagerAdapter Inside My Activity(I also should add a module for it) it works fine.
Which part is Implemented wrong?
tripsFragmentin yourMainActivity. This is the wrong scope and your mainActivity component has no access to your FragmentSubComponents graph (which the binding with matching key exists in component tries to tell you) - David Medenjak