Зависимость Android Inject (Dagger 2) внутри фрагмента с использованием @ContributesAndroidInjector - PullRequest
0 голосов
/ 10 июня 2018

Я использую Dagger 2 для внедрения зависимостей в моем проекте

Проблема в том, что я пытаюсь внедрить ViewPageAdapter внутри класса фрагмента, который зависит от FragmentManager как показано ниже:

@Inject
lateinit var mainTripsFragmentAdapter: MainTripsFragmentAdapter

Я получаю следующую ошибку сборки:

ошибка: [dagger.android.AndroidInjector.inject (T)] android.support.v4.app.FragmentManager не может быть предоставлен без аннотированного метода @ Provides.
открытый абстрактный интерфейс AppComponent расширяет dagger.android.AndroidInjector {^ android.support.v4.app.FragmentManager внедряется в com.example.presenters.main.trips.MainTripsFragmentAdapter. (FragManager,…) com.example.presenters.main.trips.MainTripsFragmentAdapter внедряется в com.example.presenters.main.trips.MainTripsFragment.mainTripsFragmentAdapter com.example.presenters.MainTripsexample.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
    }
}

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
}

Вот это 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:

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
}

Класс приложения:

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 внутри моей деятельности (я также должен добавить модуль для него), он работает нормально.
Какая часть реализована неправильно?

1 Ответ

0 голосов
/ 10 июня 2018

Здесь есть ряд проблем:

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 означает «создать элемент».При использовании 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.Вы не должны предоставлять PagerAdapters или Fragments как зависимости в графе вашего объекта.Фактически, многие классы Android, управляемые ОС, такие как FragmentManager, Activity, Fragment, BroadcastReceiver, Service, не являются хорошими кандидатами на то, чтобы стать зависимостями в графе объектов с помощью Dagger 2.

Распространенной ошибкой при запуске Dagger 2 является попытка ввести все , как будто обновление чего-либо вручную не так.Это просто невозможно в Android.

Пожалуйста, извлеките фрагменты, FragmentManager, PagerAdapter и т. Д. Из ваших модулей.Не пытайтесь предоставлять их или связывать их с помощью Dagger 2. Затем используйте Dagger 2 для внедрения объектов уровня домена или уровня данных, таких как репозитории, сервисы Retrofit и т. Д. Если вы застряли, вы всегда можете опубликовать другой вопрос.

И взгляните на официальные чертежи Google Android Architecture .Ваше решение в идеале должно выглядеть примерно так.

...