Dagger2, добавление привязки для ViewModelProvider.Factory в зависимый компонент - PullRequest
0 голосов
/ 19 марта 2020

Проблема

При попытке добавить связывание ViewModel в мультисвязку для унаследованного ViewModelFactory (созданного без области) в пределах нижней области (@FragmentScope), я продолжаю сталкиваться с этим ошибка:

java .lang.IllegalArgumentException: неизвестный класс модели com.example.app.MyFragmentVM

Что я прочитал или попробовал

(примечание: приведенный ниже список ни в коем случае не является исчерпывающим, но представляет собой два хороших примера ресурсов и советы, которые я изучил)

Я относительно новичок в работе с Dagger, поэтому мне пришлось много гуглить, чтобы попытаться понять, что происходит, но я достиг точки, когда, насколько я понимаю, что-то должно работать (?). ..

Из источников, похожих на [1], я удалил область @Singleton на ViewModelFactory, но все еще получаю afor ementioned cra sh о том, что в отображении не найден класс моделей.

Из источников, похожих на [2], я попытался укрепить мое понимание того, как работают зависимости и как элементы подвергаются воздействию зависимых компонентов. Я знаю и понимаю, как ViewModelProvider.Factory доступен для моих MyFragmentComponent и связанных с ним модулей.

Однако я не понимаю, почему @Binds @IntoMap не работает для MyFragmentVM.

Код

Позвольте мне сначала go через настройку содержимого, которое уже существует в приложении - почти ни один из них не был определен для определенных c случаев

// AppComponent
@Component(modules=[AppModule::class, ViewModelModule::class])
interface AppComponent {
    fun viewModelFactory(): ViewModelProvider.Factory

    fun inject(activity: MainActivity)
    // ... and other injections
}

// AppModule
@Module
class AppModule {
    @Provides
    @Singleton
    fun providesSomething(): Something

    // a bunch of other providers for the various injection sites, all @Singleton scoped
}

// ViewModelModule
@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindsViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(MainActivityVM::class)
    abstract fun bindsMainActivityVM(vm: MainActivityVM): ViewModel
}

// VMFactory
class ViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")

        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

И вот как я пытаюсь добавить и использовать свой @FragmentScope:

// MyFragmentComponent
@FragmentScope
@Component(
    dependencies = [AppComponent::class],
    modules = [MyFragmentModule::class, MyFragmentVMModule::class]
)
interface MyFragmentComponent {
    fun inject(fragment: MyFragment)
}

// MyFragmentModule
@Module
class MyFragmentModule {
    @Provides
    @FragmentScope
    fun providesVMDependency(): VMDependency {
        // ...
    }
}

// MyFragmentVMModule
@Module
abstract class MyFragmentVMModule {
    @Binds
    @IntoMap
    @ViewModelKey(MyFragmentVM::class)
    abstract fun bindsMyFragmentVM(vm: MyFragmentVM): ViewModel
}

// MyFragment
class MyFragment : Fragment() {
    @set:Inject
    internal lateinit var vmFactory: ViewModelProvider.Factory

    private lateinit var viewModel: MyFragmentVM

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        DaggerMyFragmentComponent.builder()
            .appComponent(MyApplication.instance.component)
            .build()
            .inject(this)

        viewModel = ViewModelProvider(this, vmFactory).get(MyFragmentVM::class.java)
    }
}

Здесь интересно отметить, что MyFragmentModule сам НЕ в конечном итоге предоставляет какие-либо уникальные инъекции для MyFragment (все они взяты из AppComponent, как сейчас). Тем не менее, он предоставляет уникальные инъекции для ViewModel, который MyFragment использует.

1 Ответ

1 голос
/ 23 марта 2020

Проблема root заключается в разнице между подкомпонентами и зависимостями компонентов .

подкомпонентами

При работе с подкомпонентами родительский компонент знает все о своих подкомпонентах. Таким образом, когда подкомпонент запрашивает мультисвязывание, родительский компонент может объединять свои вклады с вкладами подкомпонента. Это даже работает транзитивно: если подкомпонент запрашивает unscoped ViewModelProvider.Factory, внедренная карта будет включать привязки от подкомпонента. (То же самое относится к привязке @Reusable, но не к @Singleton.)

Если вы измените свои компоненты с зависимостями на подкомпоненты, все будет работать. Однако это может не соответствовать вашей желаемой архитектуре. В частности, это невозможно, если MyFragmentComponent находится в модуле Instant App.

Зависимости компонентов

При работе с зависимостями компонентов основной компонент просто экспонирует объекты с помощью методов обеспечения, а он не знает ни о каких компонентах, которые могут от него зависеть . На этот раз при запросе ViewModelProvider.Factory основной компонент не имеет доступа ни к каким привязкам @ViewModelKey, кроме своих собственных, и поэтому возвращаемое Factory не будет включать привязку MyFragmentVM.

Если MyFragmentComponent не требует привязок ViewModel из AppComponent, вы можете извлечь bindsViewModelFactory в его собственный модуль и включить его в оба компонента. Таким образом, оба компонента могут создавать свои собственные Factory независимо.

Если вам нужны некоторые ViewModel привязки из AppComponent, надеюсь, вы также можете добавить эти модули привязки в MyFragmentComponent. Если нет, вам придется открыть карту в AppComponent, а затем как-то объединить эти записи с вашими новыми привязками. Кинжал не дает хорошего способа сделать это.

...