问题
I've one LiveData
named sortOrder
and then I've another variable named myData
that observes any change to sortOrder
and populates data accordingly.
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"
}
}
Observing in Activity
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")
}
}
}
Question
- How can I replace the above scenario with
Flow/StateFlow
APIs without any change in output?
回答1:
If you fail to convert the mapped cold Flow into a hot Flow, it will restart the flow every time you collect it (like when your Activity is recreated). That's how cold flows work.
I have a feeling they will flesh out the transform functions for StateFlow/SharedFlow, because it feels very awkward to map them to cold flows and have to turn them back into hot flows.
The public Flow has to be a SharedFlow if you don't want to manually map the first element distinctly because the stateIn
function requires you to directly provide an initial state.
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)
Or you could create a separate function that is called within map
and also in a stateIn
function call.
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"))
回答2:
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
}
}
Note
Using launchWhen() functions from the Lifecycle Kotlin extensions to collect a flow from the UI layer is not always safe. When the view goes to the background, the coroutine suspends, leaving the underlying producer active and potentially emitting values that the view doesn't consume. This behavior could waste CPU and memory resources.
StateFlows are safe to collect using the launchWhen() functions since they're scoped to ViewModels, making them remain in memory when the View goes to the background, and they do lightweight work by just notifying the View about UI states. However, the problem might come with other producers that do more intensive work.
来源:https://stackoverflow.com/questions/65643610/how-to-replace-livedata-with-flow