melatonina
06/07/2021, 11:17 PMStateFlow
is a good replacement for LiveData
on Android?
There is often a noticeable delay in updates, which leads to the UI to represent incorrect information when it first shows up.
That never happened to me with LiveData
.Zach Klippenstein (he/him) [MOD]
06/07/2021, 11:54 PMtateisu
06/08/2021, 1:34 AMAlbert Chang
06/08/2021, 3:41 AMgildor
06/08/2021, 5:40 AMLiveData supports lifecycle, Flow does notIt’s completely incorrect Flow was built with structured concurrency from ground up which is much more strict and powerful version than integration with lifecycle on LiveData it just doesn’t have exactly the same semantics as LiveData by default, but it’s not an issue of Flow, it’s just a matter of different semantics
melatonina
06/08/2021, 8:50 AMDispatchers.Main
, but even changing it to Dispatchers.Main.immediate
, the problem remains.
@Albert Chang Yes, cross-posting was a bad idea. Sorry.
I'm attaching a video which illustrates the problem. When the RecyclerView
item view appear on the screen, the text is missing and an icon which should be hidden is visible.
The video is slowed down to 1/10 of the original speed.<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="<http://schemas.android.com/apk/res/android>"
xmlns:tools="<http://schemas.android.com/tools>"
xmlns:app="<http://schemas.android.com/apk/res-auto>">
<data>
<import type="android.view.View" alias="View" />
<variable
name="viewModel"
type="com.example.ProjectViewModel" />
</data>
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp"
android:layout_marginHorizontal="10dp"
android:background="#fff"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/montserrat"
android:text="@{viewModel.stateFlow.projectEntity.name}"
android:textSize="24sp"
android:textStyle="bold"
tools:text="Project name" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_cloud_24"
android:visibility="@{viewModel.isRemoteFlow ? View.VISIBLE : View.INVISIBLE}"
/>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
and here is the ViewModel
definition:
class ProjectViewModel(
initialState: ProjectState2
) : ViewModel() {
companion object {
fun remote(projectEntity: ProjectEntity) =
ProjectViewModel(ProjectState2.Remote.Available(projectEntity))
fun local(projectEntity: ProjectEntity) =
ProjectViewModel(ProjectState2.Local.Available(projectEntity))
}
private val stateMutableFlow = MutableStateFlow<ProjectState2>(initialState)
val stateFlow : StateFlow<ProjectState2> get() = stateMutableFlow
var state by stateMutableFlow
private set
val projectId get() = state.projectId
val isRemoteFlow = state(stateFlow.map { it.isRemote }, false)
val isRemote by isRemoteFlow
}
where state()
is defined as:
fun <T> ViewModel.state(flow : Flow<T>, initialValue: T) =
flow.stateIn(viewModelScope, SharingStarted.Eagerly, initialValue)
state
is never updated, so the underlying MutableStateFlow
never changes its state after inizialization.gildor
06/08/2021, 9:05 AMmelatonina
06/08/2021, 9:06 AMclass ViewHolder(
private val binding: ProjectItemBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bindTo(projectViewModel: ProjectViewModel) {
binding.viewModel = projectViewModel
}
}
gildor
06/08/2021, 9:07 AMmelatonina
06/08/2021, 9:08 AMgildor
06/08/2021, 9:11 AMmelatonina
06/08/2021, 1:46 PMoverride fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(
ProjectItemBinding.inflate(
LayoutInflater.from(viewGroup.context),
viewGroup,
false
)
)
now it's:
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(
ProjectItemBinding.inflate(
LayoutInflater.from(viewGroup.context),
viewGroup,
false
).apply {
lifecycleOwner = this@ProjectListAdapter.lifecycleOwner
}
)
gildor
06/09/2021, 3:18 AMmelatonina
06/10/2021, 8:28 AMgildor
06/10/2021, 8:28 AM