【问题标题】:Android Inject Dependency (Dagger 2) Inside Fragment Using @ContributesAndroidInjectorAndroid Inject Dependency (Dagger 2) Inside Fragment Using @ContributesAndroidInjector
【发布时间】:2018-11-19 17:25:39
【问题描述】:

我在我的项目中使用Dagger 2 进行依赖注入

问题是当我尝试在依赖于FragmentManager 的片段类中注入ViewPageAdapter 时,如下所示:

@Inject
lateinit var mainTripsFragmentAdapter: MainTripsFragmentAdapter

我收到以下构建错误:

错误:[dagger.android.AndroidInjector.inject(T)] android.support.v4.app.FragmentManager 不能在没有@Provides-annotated 方法的情况下提供。
公共抽象接口 AppComponent 扩展 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 天,阅读了很多文章,但我无法弄清楚问题出在哪里!
相关代码我贴一下,太长见谅

需要PagerAdapterFragment

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
}

ActivityBuilder:

@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(我也应该为它添加一个模块)它工作正常。
哪一部分是实施错误的?

【问题讨论】:

  • 我以老式的方式使用 dagger 2,但是您在哪里提供 MainTripsFragmentAdapter 在您的模块中?
  • 如果您查看错误,您可以在底部看到问题是您尝试在您的MainActivity 中注入tripsFragment。这是错误的范围,您的 mainActivity 组件无法访问您的 FragmentSubComponents 图(与匹配键的绑定存在于组件中试图告诉您)
  • @Debanjan 我没有在任何地方提供它,但我注入了构造函数,所以我认为 dagger 将满足它的依赖项
  • @DavidMedenjak 因为我是 dagger2 和 DI 的初学者,所以我无法真正理解范围是如何相互关联的。你能告诉我这是一种解决方法还是我做错了

标签: android dependency-injection kotlin dagger-2


【解决方案1】:

这里有很多问题:

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
}

PagerAdapter 中的getItem 表示“创建项目”。当您使用PagerAdapter 时,您将创建片段委托给适配器。您不应该编写代码来缓存和存储片段,这是FragmentManager 的工作。

您的FragmentAdapter 应该是这样的:

    override fun getItem(position: Int) = when (position) { 
        0 -> MainCurrentTripsFragment.instantiate()
        1 -> MainHistoryTripsFragment.instantiate()
        else -> MainCurrentTripsFragment.instantiate()
    }

instantiate 方法在 Fragments 内的伴随对象中声明:

class MainCurrentTripsFragment: Fragment() {
    companion object {
        @JvmStatic
        fun instantiate(args: Bundle?) {
            val frag = MainCurrentTripsFragment()
            frag.arguments = args
        }
    }
}

这是在 Android 中将 PagerAdapter 与 Fragments 一起使用的标准习惯用法。您不应该在对象图中提供 PagerAdapters 或 Fragments 作为依赖项。事实上,许多由操作系统控制的 Android 类,如 FragmentManagerActivityFragmentBroadcastReceiverService,都不是使用 Dagger 2 成为对象图中依赖项的好选择。

开始使用 Dagger 2 时的一个常见错误是尝试注入 everything,就好像手动更新任何东西都是错误的一样。这在 Android 中是不可能的。

请从您的模块中取出 Fragments、FragmentManager、PagerAdapter 等。不要尝试使用 Dagger 2 提供这些或绑定这些。然后使用 Dagger 2 注入域层或数据层对象,如存储库、改造服务等。如果你遇到困难,你可以随时发布另一个问题。

看看官方的Google Android Architecture blueprints。理想情况下,您的解决方案应该类似于它们。

【讨论】:

  • 虽然这不是问题的直接答案,但我从你的回答中学到了很多东西,所以你通常的意思是 android 的东西不应该由 dagger 提供,我们应该简单地创建新的实例他们在我们需要的任何地方?
  • @Mosius 我意识到这不是一个完整的答案,但您遇到如此多困难的部分原因是尝试使用 Dagger 2 使所有内容都成为依赖项。FragmentManager 不需要成为一个依赖,只需调用getFragmentManager。与PagerAdapter 相同,您可以在onCreate 中更新它
  • 如果您查看示例项目,您会很好地了解应该和不应该使用 Dagger 2 作为依赖项注入的内容
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多