Как заменить LiveData на Flow
Есть один
LiveData
с именем, а затем у меня есть еще одна переменная с именем
myData
что замечает любые изменения в
sortOrder
и соответственно заполняет данные.
class TestViewModel @ViewModelInject constructor() : ViewModel() {
private val sortOrder = MutableLiveData<String>()
val myData = sortOrder.map {
Timber.d("Sort order changed to $it")
"Sort order is $it"
}
init {
sortOrder.value = "year"
}
}
Наблюдение в действии
class TestActivity : AppCompatActivity() {
private val viewModel: TestViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
// Observing data
viewModel.myData.observe(this) {
Timber.d("Data is : $it")
}
}
}
Вопрос
- Как я могу заменить описанный выше сценарий на
Flow/StateFlow
API без каких-либо изменений в выводе?
3 ответа
Если вам не удалось преобразовать отображенный холодный поток в горячий поток, он будет перезапускать поток каждый раз, когда вы его собираете (например, при воссоздании вашего Activity). Вот как работают холодные потоки.
У меня есть ощущение, что они конкретизируют функции преобразования для StateFlow / SharedFlow, потому что очень неудобно сопоставлять их с холодными потоками и возвращать их обратно в горячие потоки.
Общедоступный поток должен быть SharedFlow, если вы не хотите вручную четко отображать первый элемент, потому что функция требует, чтобы вы напрямую указали начальное состояние.
private val sortOrder = MutableStateFlow("year")
val myData = sortOrder.map {
Timber.d("Sort order changed to $it")
"Sort order is $it"
}.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
Или вы можете создать отдельную функцию, которая вызывается внутри
map
а также в
stateIn
вызов функции.
private val sortOrder = MutableSharedFlow<String>()
private fun convertSortOrder(order: String): String {
Log.d("ViewModel", "Sort order changed to $order")
return "Sort order is $order"
}
val myData = sortOrder.map {
convertSortOrder(it)
}.stateIn(viewModelScope, SharingStarted.Eagerly, convertSortOrder("year"))
С точки зрения фрагмента / активности вы должны создать задание, которое собирает поток в
onStart()
и отменить в
onStop()
. С использованием
lifecycleScope.launchWhenStarted
сохранит поток активным даже в фоновом режиме.
Используйте библиотеку bindin для перехода на
Flow
легко. Я пристрастен, хотя.
См. Статью на Medium об этом.
yourViewModel
private val _sortOrder = MutableStateFlow("")
val sortOrder: StateFlow<String> = _sortOrder
yourActivity
lifecycleScope.launchWhenStarted {
// Triggers the flow and starts listening for values
yourViewModel.sortOrder.collect { sortOrder ->
// your code
}
}
Примечание
Использование функций launchWhen() из расширений Lifecycle Kotlin для сбора потока с уровня пользовательского интерфейса не всегда безопасно. Когда представление переходит в фоновый режим, сопрограмма приостанавливается, оставляя активным базовый производитель и потенциально генерируя значения, которые представление не потребляет. Такое поведение может привести к потере ресурсов ЦП и памяти.
StateFlows безопасно собирать с помощью функций launchWhen(), поскольку они привязаны к ViewModels, заставляя их оставаться в памяти, когда View переходит в фоновый режим, и они выполняют легкую работу, просто уведомляя View о состояниях пользовательского интерфейса. Однако проблема может возникнуть у других производителей, которые выполняют более интенсивную работу.