Предоставление ViewModels "без" Factory - PullRequest
2 голосов
/ 24 апреля 2020

После прочтения этого великого среднего из Elye я начинаю думать о том, как я справляюсь с ViewModels (с шаблоном или без него). Обычно я собирался использовать подход Dagger Android, более подробный с фабриками Assisted Injected ViewModels и generic c, et c. Тем не менее, у меня была проблема .
Во всяком случае, во всем посте я узнал, что могу решить мою проблему, удалив Assisted Inject logi c из моего ViewModels и удалив зависимости, введенные конструктором. по моему ViewModelFactory. С этим я решил проблему с пакетом, но - и очень большой, но ^^ - установка стала более многословной, так как теперь мне пришлось создавать фабрику для каждого ViewModel - облома.

К счастью, автор показывает нам (меня интересуют только Dagger решения), что мы можем фактически отбросить все фабричные логики c и использовать «Простое решение на основе кинжала» , где - проще говоря - мы перемещаем все конструктор вставил зависимости в инжекцию поля (да, они будут опубликованы c, это также вызывает у меня зуд, но оно превосходит весь шаблон, и вы можете сделать их protected). SavedStateHandle остается в конструкторе, а by viewModels() приходит на помощь (в Activity или Fragment).

My BaseActivity :

abstract class BaseActivity<VM : ViewModel, ...> : ... {

    @Inject
    lateinit var viewModelFactory: ViewModelFactory
    protected val viewModel: VM by lazy { ViewModelProvider(this, viewModelFactory).get(getViewModelClass()) }

    protected abstract fun getViewModelClass(): Class<VM>
    ...
}

Я начал с попытки изменить это на:

protected val viewModel: VM by viewModels(getViewModelClass())

Но это не сработало, поэтому я скопировал viewModels() и изменил его на:

private fun viewModels(
    clazz: KClass<VM>,
    factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    return ViewModelLazy(clazz, { viewModelStore }, factoryPromise)
}

protected val viewModel: VM by viewModels(getViewModelClass())

И это сработало! Но было выдано предупреждение ...:

Вызов не финальной функции в конструкторе.

Итак, я добавил это:

private fun initViewModel(): VM {
    val v: VM by viewModels(getViewModelClass())
    return v
}

protected val viewModel: VM by lazy { initViewModel() }

И наконец BaseActivity ViewModel logi c стал:

abstract class BaseActivity<VM : ViewModel,...> : ... {

    private fun viewModels(
        clazz: KClass<VM>,
        factoryProducer: (() -> ViewModelProvider.Factory)? = null
    ): Lazy<VM> {
        val factoryPromise = factoryProducer ?: {
            defaultViewModelProviderFactory
        }
        return ViewModelLazy(clazz, { viewModelStore }, factoryPromise)
    }

    private fun initViewModel(): VM {
        val v: VM by viewModels(getViewModelClass(), getViewModelFactory())
        return v
    }

    protected val viewModel: VM by lazy { initViewModel() }


    protected fun getViewModelFactory(): (() -> ViewModelProvider.Factory)? = null
       ...
   }

Нет предупреждений, нет ошибок во время компиляции, нет ошибок во время выполнения - это работает!

Почему мои Activities / Fragments не могут справиться со своими ViewModel? Они могут, но:

abstract class BaseActivity<VM : ViewModel, ViewBinding : ViewDataBinding> ... {
    ...
    protected val viewModel: VM by lazy { initViewModel() }
    protected val binding: ViewBinding by lazy { DataBindingUtil.setContentView<ViewBinding>(this, getLayoutResId()) }
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        ...
        super.onCreate(savedInstanceState)

        binding.lifecycleOwner = this
        binding.setVariable(getViewModelVariableId(), viewModel)
        ...
        initViews(binding, viewModel)
    }
}

У меня есть такой лог установки c, и я не хочу его повторять.

Итак, что вы думаете об этом, полезном или чрезмерном проектировании? Буду ли я столкнуться с неприятностями?

...