简体   繁体   中英

How to replace LiveData with Flow

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?

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"))

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?

From the point of the fragment/activity, you have to create a job that collects the flow in onStart() and cancel it in onStop() . Using the lifecycleScope.launchWhenStarted will keep the flow active even in the background.

Use the bindin library to migrate to Flow with ease. I'm biased, tho.

See an article on medium about it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM