Фрагмент ViewModel воссоздает вращение экрана

Я создаю приложение с новейшими компонентами архитектуры Android. Я использую firebase firestore в качестве базы данных с навигацией по реактивному ранцу (нижняя навигация). Я успешно могу отображать данные из БД. Но всякий раз, когда я поворачиваю экран mt, фрагмент хранилища воссоздается и отправляет запрос в БД.

Репозиторий

    override fun getAllStores() = callbackFlow<State<List<Store>>> {
    // Emit loading state
    send(State.loading())

    val listener = remoteDB.collection(Constants.COLLECTION_STORES)
        .addSnapshotListener { querySnapshot, exception ->
            querySnapshot?.toObjects(Store::class.java)?.let { store ->
                // Emit success state with data
                offer(State.success(store))
            }
            exception?.let {
                // emit exception with message
                offer(State.failed(it.message!!))
                cancel()
            }
        }
    awaitClose {
        listener.remove()
        cancel()
    }

}.catch {
    // Thrown exception on State Failed
    emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)

Модель представления

@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {

fun getAllStores() = repository.getAllStores()

}

Сохранить фрагмент

@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreFragment : Fragment(R.layout.fragment_store) {
private lateinit var storeAdapter: StoreAdapter
private val viewModel: StoreViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (activity as MainActivity).supportActionBar?.title = getString(R.string.store_title)

    setUpRV()

    // get all stores
    lifecycleScope.launch {
        getAllStores()
    }

}

private suspend fun getAllStores() {
    viewModel.getAllStores().collect { state ->
        when (state) {
            is State.Loading -> {
                store_progress.show()
            }

            is State.Success -> {
                storeAdapter.differ.submitList(state.data)
                store_progress.animate().alpha(0f)
                        .withEndAction {
                            store_rv.animate().alpha(1f)
                            store_progress.hide()
                        }

            }
            is State.Failed -> {
                store_progress.hide()
                activity?.toast("Failed! ${state.message}")
            }
        }
    }
}

private fun setUpRV() {
    storeAdapter = StoreAdapter()
    store_rv.apply {
        adapter = storeAdapter
        addItemDecoration(SpacesItemDecorator(16))
    }
}

}

Основное действие (навигационный график)

@InternalCoroutinesApi
@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)

    // init bottom navigation
    bottom_navigation.setupWithNavController(nav_host_fragment.findNavController())
   
}
}

Каждый раз он воссоздает мой фрагмент. Я не хочу сохранять или сохранять какие-либо представления с помощью методов. Поскольку ViewModel используется для защиты просмотра при повороте экрана. Пожалуйста, дайте мне знать любые советы и рекомендации. Заранее спасибо ;)


person Spikeysanju    schedule 26.07.2020    source источник
comment
Что заставляет вас думать, что ViewModel воссоздается? У вас есть журнал создания ViewModel? Flow сам по себе не хранит никакого состояния (например, в отличие от LiveData), так что, возможно, то, что вы видите, связано с этим?   -  person ianhanniballake    schedule 26.07.2020
comment
Я использовал LiveData для того же проекта, и он отлично работает. Когда я использую Flow, меня это поразило. Я использую ведение журнала во фрагменте. Есть ли у вас какие-либо предложения по решению этой проблемы? заранее спасибо   -  person Spikeysanju    schedule 26.07.2020


Ответы (2)


Flow сам по себе не имеет состояния - это ключевое отличие между ним и LiveData. Это означает, что после завершения вашего collect следующий collect запускает callbackFlow с нуля.

Именно поэтому артефакт lifecycle-livedata-ktx содержит asLiveData() расширение, которое позволяет вам продолжать использовать Flow на уровне репозитория, сохраняя при этом свойства состояния (и жизненного цикла) LiveData для вашего пользовательского интерфейса:

@ExperimentalCoroutinesApi
@InternalCoroutinesApi
class StoreViewModel(private val repository: DBInterface = Repo()) : ViewModel() {
    fun getAllStores() = repository.getAllStores().asLiveData()
}

Вы бы изменили свой код пользовательского интерфейса, чтобы продолжать использовать LiveData и observe().

Kotlin работает над операцией shareIn, которая будет разрешить вашему ViewModel сохранять состояние Flow. Это позволит вам использовать Flow на всех уровнях вашего приложения без повторного запроса информации с нуля, когда фрагмент/активность, вызывающая collect, уничтожается и воссоздается.

person ianhanniballake    schedule 26.07.2020

вы можете добавить android:configChanges="orientation|screenSize|screenLayout" в свой манифест для активности. это должно предотвратить перезапуск при изменении ориентации.

проверьте этот сайт, а также здесь есть некоторая информация.

person Felix    schedule 26.07.2020
comment
Работает как шарм. Спасибо друг - person Spikeysanju; 26.07.2020
comment
Это имеет серьезные проблемы, если у вас когда-либо будут разные макеты в зависимости от размера экрана / любого квалификатора ресурса, поскольку вам нужно будет вручную применять эти изменения, и на самом деле это скорее обходной путь, чем фактическое исправление основной проблемы. - person ianhanniballake; 26.07.2020