How to replace LiveData with Flow

↘锁芯ラ 提交于 2021-02-08 09:13:31

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!