Новая архитектура с Dagger и Kotlin

У меня проблема с компонентами новой архитектуры в Kotlin, когда я создаю компонент ViewModel рекомендованным способом (в методе onCreate ()), результат будет следующим:

  • после изменения ориентации активности я получил тот же экземпляр ViewModel, что и раньше

Вот как я создаю это

override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_list)

    val arrayMap = ArrayMap<Class<out ViewModel>, ViewModel>()
    arrayMap.put(ListViewModel::class.java, ListViewModel(webApi, repoDao))
    val factory = ViewModelFactory(arrayMap)
    listViewModel = ViewModelProviders.of(this, factory).get(ListViewModel::class.java)

    listViewModel.items.observe({ this.lifecycle }) {
        Toast.makeText(this, it?.joinToString { it + " " } ?: "null", Toast.LENGTH_SHORT).show()
    }

Но когда я использовал Dagger для внедрения ListViewModel, я получал новый экземпляр ListViewModel каждый раз при воссоздании Activity. Вот код Dagger ListActivityModel.

@Module @ListActivityScopeclass ListActivityModule {
@Provides
@ListActivityScope
fun provideListViewModel(webApi: WebApi, repoDao: RepoDao, listActivity: ListActivity): ListViewModel {
    val arrayMap = ArrayMap<Class<out ViewModel>, ViewModel>()
    arrayMap.put(ListViewModel::class.java, ListViewModel(webApi, repoDao))
    val factory = ViewModelFactory(arrayMap)
    val result =  ViewModelProviders.of(listActivity, factory).get(ListViewModel::class.java)
    return result
}

} Тогда метод ListActivity onCreate () выглядит так:

override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_list)
    listViewModel.items.observe({ this.lifecycle }) {
        Toast.makeText(this, it?.joinToString { it + " " } ?: "null", Toast.LENGTH_SHORT).show()
    }
}

И вот что я заметил после регистрации:

D/ListActivity: ---> onCreate() ListActivity: = [com.example.dom.app.new_arch.ListActivity@a0f2778]
D/ListActivity: ---> onCreate() listViewModel: = [com.example.dom.app.new_arch.ListViewModel@54a8e51]

//Activity orientation changes

E/ViewModelStores: Failed to save a ViewModel for com.example.dom.app.new_arch.ListActivity@a0f2778
D/ListActivity: ---> onCreate() ListActivity: = [com.example.dom.app.new_arch.ListActivity@6813433]
D/ListActivity: ---> onCreate() listViewModel: = [com.example.dom.app.new_arch.ListViewModel@55cf3f0]

Я получил ошибку:

ViewModelStores: не удалось сохранить ViewModel для

происходит из Android-класса HolderFragment с пакетом android.arch.lifecycle.

Есть что-то, чего мне не хватало при работе с Dagger и новыми арочными компонентами?


person anddev    schedule 01.11.2017    source источник
comment
AndroidInjection.inject () создает новый компонент для каждого нового экземпляра действия. Именно поэтому я им не пользуюсь.   -  person EpicPandaForce    schedule 02.11.2017
comment
Это не проблема, поскольку ViewModel предоставляется статическим фабричным методом: ViewModelProviders.of (listActivity, factory) .get (ListViewModel :: class.java)   -  person anddev    schedule 03.11.2017
comment
Я столкнулся с той же проблемой, по неизвестной причине создание модели представления в модуле с использованием dagger-android-support приводит к ошибке ViewModelStores   -  person Ismael Di Vita    schedule 04.11.2017
comment
@IsmaelDiVita нашли ли вы какое-либо решение или проблему, где они подробно описаны?   -  person anddev    schedule 05.11.2017


Ответы (3)


Проблема связана с порядком внедрения кинжала и создания активности. Реализация модели представления опирается на невизуальный фрагмент для идентификации. Внедрение viewModelProvider до завершения действия onCreate не позволяет завершить эту ассоциацию.

Поскольку super.onCreate вряд ли зависит от того, что вы вводите, попробуйте ввести его после вызова super.onCreate, и все будет в порядке.

У меня была такая же проблема, и я решил ее этим изменением по порядку.

В частности, из вашего кода вместо:

override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_list)

идти с:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    AndroidInjection.inject(this)
    setContentView(R.layout.activity_list)

JP

person John Michael Pirie    schedule 27.02.2018

Я делаю это только с помощью ViewModelFactory, используя Dagger. Затем он вводится в действие, и вы вызываете ViewModelProviders.of(listActivity, factory).get(ListViewModel::class.java) оттуда. Причина, по которой ваш подход не работает, заключается в том, что AndroidInjection.inject() создаст ViewModel перед onCreate, что приведет к неопределенному поведению.

См. Также: https://github.com/googlesamples/android-architecture-components/issues/202

person mcassiano    schedule 07.11.2017
comment
Спасибо за ответ, он правильный. Я знаю ваше решение, но я пытался предоставить ViewModel от Dagger, чтобы избежать шаблонного кода в методе onCreate (). - person anddev; 08.11.2017

Я не использую AndroidInjection.inject(), потому что он создает новый компонент Dagger. Я создаю компонент Dagger в классе Application и использую этот экземпляр компонента для вызова inject во всех других местах приложения. Таким образом, ваши синглтоны инициализируются только один раз.

person Damia Fuentes    schedule 21.11.2017