Skip to content

Instantly share code, notes, and snippets.

@luciofm
Last active June 13, 2020 14:33
Show Gist options
  • Save luciofm/cc463f38f488c4c4fccf531b53c6ac10 to your computer and use it in GitHub Desktop.
Save luciofm/cc463f38f488c4c4fccf531b53c6ac10 to your computer and use it in GitHub Desktop.
How to run LiveData transformations on a coroutine
class ChatRoomsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, factory).get(ChatRoomsViewModel::class.java)
subscribeUi()
}
private fun subscribeUi() {
val adapter = RoomsAdapter()
recycler_view.layoutManager = LinearLayoutManager(it)
recycler_view.addItemDecoration(DividerItemDecoration(it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)))
recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = adapter
viewModel.getChatRooms().observe(viewLifecycleOwner, Observer { rooms ->
rooms?.let {
adapter.values = it
}
})
}
}
class ChatRoomsViewModel(
private val repository: ChatRoomsRepository,
private val mapper: RoomMapper
) : ViewModel() {
private val ordering: MutableLiveData<ChatRoomsRepository.Order> = MutableLiveData()
private val runContext = newSingleThreadContext("chat-rooms-view-model")
init {
ordering.value = ChatRoomsRepository.Order.ACTIVITY
}
fun getChatRooms(): LiveData<List<ItemHolder<*>>> {
return Transformations.switchMap(ordering) { order ->
repository.getChatRooms(order)
.nonNull()
.distinct()
.transform(runContext) { rooms ->
rooms?.let {
mapper.map(rooms, order.isGrouped())
}
}
}
}
fun setOrdering(order: ChatRoomsRepository.Order) {
ordering.value = order
}
}
class TransformedLiveData<Source, Output>(
private val runContext: CoroutineContext = CommonPool,
private val source: LiveData<Source>,
private val transformation: (Source?) -> Output?)
: LiveData<Output>() {
private var job: Job? = null
private val observer = Observer<Source> { source ->
job?.cancel()
job = launch(runContext) {
transformation(source)?.let { transformed ->
// Could have used postValue instead, but using the UI context I can guarantee that
// a canceled job will never emit values.
withContext(UI) {
value = transformed
}
}
}
}
override fun onActive() {
source.observeForever(observer)
}
override fun onInactive() {
job?.cancel()
source.removeObserver(observer)
}
}
fun <Source, Output> LiveData<Source>.transform(
runContext: CoroutineContext = CommonPool,
transformation: (Source?) -> Output?) = TransformedLiveData(runContext, this, transformation)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment