android - Android Inject Dependency (Dagger 2) Inside Fragment Using @ContributesAndroidInjector
问题描述
我在我的项目中使用Dagger 2进行依赖注入
问题是当我试图ViewPageAdapter
在片段类中注入一个依赖于FragmentManager
如下的片段类时:
@Inject
lateinit var mainTripsFragmentAdapter: MainTripsFragmentAdapter
我收到以下构建错误:
错误:[dagger.android.AndroidInjector.inject(T)] android.support.v4.app.FragmentManager 不能在没有@Provides-annotated 方法的情况下提供。
公共抽象接口 AppComponent extends dagger.android.AndroidInjector { ^ android.support.v4.app.FragmentManager 在 com.example.presenters.main.trips.MainTripsFragmentAdapter.(fragmentManager, ...) com.example.presenters.main.trips 注入.MainTripsFragmentAdapter 在 com.example.presenters.main.trips.MainTripsFragment.mainTripsFragmentAdapter 注入 com.example.presenters.main.trips.MainTripsFragment 在 com.example.presenters.main.MainActivity.tripsFragment com.example.presenters.main 注入.MainActivity 在 dagger.android.AndroidInjector.inject(arg0) 注入 组件中存在具有匹配键的绑定:com.example.di.FragmentBuilder_BindMainTripsFragment$app_debug.MainTripsFragmentSubcomponent
我已经研究了 3 天,我已经阅读了很多文章,但我无法弄清楚问题出在哪里!
相关代码我贴一下,太长见谅
其中Fragment
需要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
}
}
基本片段:
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
}
这是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
}
这里是 DI 相关的类:
应用组件:
@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
}
}
片段生成器:
@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
}
片段模块:
@Module
class FragmentModule {
@Provides
fun provideFragmentManager(fragment: Fragment) = fragment.childFragmentManager
}
活动构建器:
@Module
abstract class ActivityBuilder {
@ContributesAndroidInjector
internal abstract fun contributeMainActivity(): 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()
}
}
基本活动:
@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
}
应用类:
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)
}
}
}
一点是,如果我@Inject
在我的活动中使用 PagerAdapter(我也应该为它添加一个模块)它工作正常。
哪个部分实施错误?
解决方案
这里有很多问题:
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 //no!!
1 -> mainHistoryTripsFragment
else -> mainCurrentTripsFragment
}
override fun getCount() = 2
}
getItem
意思是PagerAdapter
“创建项目”。当您使用 a 时,您将Fragments 的创建PagerAdapter
委托给适配器。您不应该编写代码来缓存和存储片段,这是.FragmentManager
相反,您FragmentAdapter
应该看起来像这样:
override fun getItem(position: Int) = when (position) {
0 -> MainCurrentTripsFragment.instantiate()
1 -> MainHistoryTripsFragment.instantiate()
else -> MainCurrentTripsFragment.instantiate()
}
该instantiate
方法在片段内的伴随对象中声明的位置:
class MainCurrentTripsFragment: Fragment() {
companion object {
@JvmStatic
fun instantiate(args: Bundle?) {
val frag = MainCurrentTripsFragment()
frag.arguments = args
}
}
}
PagerAdapter
这是在 Android 中使用 with Fragments的标准习惯用法。您不应该在对象图中提供 PagerAdapters 或 Fragments 作为依赖项。事实上,许多由操作系统控制的 Android 类,如FragmentManager
、Activity
、Fragment
、BroadcastReceiver
,Service
都不是使用 Dagger 2 成为对象图中的依赖项的好选择。
开始使用 Dagger 2 时的一个常见错误是尝试注入所有内容,好像手动更新任何内容都是错误的。这在 Android 中是不可能的。
请从您的模块中取出 Fragments、FragmentManager、PagerAdapter 等。不要尝试使用 Dagger 2 提供这些或绑定这些。然后使用 Dagger 2 注入域层或数据层对象,如存储库、改造服务等。如果你遇到困难,你可以随时发布另一个问题。
并查看官方的Google Android 架构蓝图。理想情况下,您的解决方案应该看起来像它们。
推荐阅读
- android - Jetpack Compose:支持所有屏幕尺寸的最佳方式是什么?
- java - gradle build 运行时如何打印到标准输出?
- spring-security - 使用 Spring Security OAuth2 和 Vaadin 流程登录 21
- gooddata - 如何在 GoodData 中直接更改格式
- reactjs - 反应方法绑定
- c++ - 如何将用户为板中的行和列选择的内容
- ios - 以编程方式呈现全屏
- wordpress - 将 ACF 字段添加到 REST API 以进行递归发布
- bash - 如何将 stdout 和 stderr 分别传送到两个不同的进程,同时让它们出现在终端中?
- python - pandas 1.1.5 和 1.3.4 之间的哪些变化改变了 set_index / reset_index 过程?