当我使用 时collectAsState(),collect {}仅在传递新列表时触发,而不是在修改和发出列表时触发。
查看模型
@HiltViewModel
class MyViewModel @Inject constructor() : ViewModel() {
val items = MutableSharedFlow<List<DataItem>>()
private val _items = mutableListOf<DataItem>()
suspend fun getItems() {
_items.clear()
viewModelScope.launch {
repeat(5) {
_items.add(DataItem(it.toString(), "Title $it"))
items.emit(_items)
}
}
viewModelScope.launch {
delay(3000)
val newItem = DataItem("999", "For testing!!!!")
_items[2] = newItem
items.emit(_items)
Log.e("ViewModel", "Updated list")
}
}
}
data class DataItem(val id: String, val title: String)
Run Code Online (Sandbox Code Playgroud)
可组合的
@Composable
fun TestScreen(myViewModel: MyViewModel) {
val myItems by myViewModel.items.collectAsState(listOf())
LaunchedEffect(key1 = true) …Run Code Online (Sandbox Code Playgroud) kotlin kotlin-coroutines android-jetpack-compose kotlin-flow android-jetpack-compose-list
我试图在我的类中保留可变的状态流,但是当我对其应用任何方法时,它将转换为不可变的Flow<T>:
class MyClass : Listener<String> {
private val source = Source()
val flow: Flow<String?>
get() = _flow
// region listener
override fun onUpdate(value: String?) {
if (value!= null) {
// emit object changes to the flow
// not possible, because the builder operators on the flow below convert it to a `Flow` and it doesn't stay as a `MutableSharedFlow` :(
_flow.tryEmit(value)
}
}
// end-region
@OptIn(ExperimentalCoroutinesApi::class)
private val _flow by lazy {
MutableStateFlow<String?>(null).onStart {
emitAll(
flow<String?> {
val …Run Code Online (Sandbox Code Playgroud) 我正在尝试让显示缩放功能与 JetPack Compose 一起使用。我有一个 ViewModel 将共享首选项值公开为流,但这绝对是不正确的,如下所示:
@HiltViewModel
class MyViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel() {
private val _densityFactor: MutableStateFlow<Float> = MutableStateFlow(1.0f)
val densityFactor: StateFlow<Float>
get() = _densityFactor.asStateFlow()
private fun getDensityFactorFromSharedPrefs(): Float {
val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
return sharedPreference.getFloat("density", 1.0f)
}
// This is what I look at and go, "this is really bad."
private fun densityFactorFlow(): Flow<Float> = flow {
while (true) {
emit(getDensityFactorFromSharedPrefs())
}
}
init {
viewModelScope.launch(Dispatchers.IO) {
densityFactorFlow().collectLatest { …Run Code Online (Sandbox Code Playgroud) 当用户快速点击按钮时,showDialog() 方法会在彼此的顶部显示多次,因此当您关闭它时,它后面还有另一个。我正在寻找一种方法来忽略第二次点击 1 秒而不使用处理程序或检查前一次点击的时间。
//Button that opens a dialog
button.setOnClickListener {
showDialog()
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找使用 Kotlin 协程或 Kotlin 流程的解决方案以供将来实现。
我想读取文件内容并为其内容的每一行发出一个流程。所以,我必须实现一个具有以下签名的函数:
fun InputStream.linesToFlow(): Flow<String>
Run Code Online (Sandbox Code Playgroud)
有什么办法可以实现这个功能吗?
我有代码应该使用流将 SharedPreferences 更改为可观察的存储,所以我有这样的代码
internal val onKeyValueChange: Flow<String> = channelFlow {
val callback = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
coroutineScope.launch {
//send(key)
offer(key)
}
}
sharedPreferences.registerOnSharedPreferenceChangeListener(callback)
awaitClose {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(callback)
}
}
Run Code Online (Sandbox Code Playgroud)
或这个
internal val onKeyValueChange: Flow<String> = callbackFlow {
val callback = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
coroutineScope.launch {
send(key)
//offer(key)
}
}
sharedPreferences.registerOnSharedPreferenceChangeListener(callback)
awaitClose {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(callback)
}
}
Run Code Online (Sandbox Code Playgroud)
然后我观察令牌、userId、companyId 的这个偏好,然后登录,但有一点很奇怪,因为我需要构建应用程序三次,比如更改令牌不会导致 tokenFlow 发出任何东西,然后第二次新的 userId 不会导致 userIdFlow 发出任何东西,然后在第三次登录后,我可以注销/登录并且它可以工作。注销时,我正在清除 prefs 令牌、userId、companyId 中的所有 3 个属性存储。
MutableStateFlow如果更新值等于旧值(源),则不会通知收集器。我找到了解决此问题的方法,但对于复杂值来说它不能很好地扩展。
解决方法:使用/重复数据类copy()和列表。toList()toMutableList()
WorkoutRoutine示例 1:使用解决方法重命名的简单数据类name。这里没什么问题。
data class WorkoutRoutine(
var name: String,
)
val workoutRoutine = MutableStateFlow(WorkoutRoutine("Initial"))
workoutRoutine.value.name = "Updated" // Doesn't notify collectors
workoutRoutine.value = workoutRoutine.value.copy(name = "Updated") // Workaround: works
Run Code Online (Sandbox Code Playgroud)
示例 2:WorkoutRoutine具有多个依赖项的复杂数据类,使用解决方法将 a 添加Set到Exercisein 中WorkoutRoutine:这需要大量的copy()andtoMutableList()调用,这使得代码不可读。
data class WorkoutRoutine(
var name: String,
var exercises: MutableList<Exercise> = mutableListOf(Exercise())
)
data class Exercise(
var sets: MutableList<Set> = mutableListOf(Set())
)
data …Run Code Online (Sandbox Code Playgroud) 我对 Kotlin 协程和流程以及它们的单元测试很陌生。我有一个非常简单的测试:
@Test
fun debounce(): Unit = runBlocking {
val state = MutableStateFlow("hello")
val debouncedState = state.debounce(500).stateIn(this, SharingStarted.Eagerly, "bla")
assertThat(debouncedState.value).isEqualTo("bla")
state.value = "good bye"
// not yet...
assertThat(debouncedState.value).isEqualTo("bla")
delay(600)
// now!
assertThat(debouncedState.value).isEqualTo("good bye")
// cannot close the state flows :(
cancel("DONE")
}
Run Code Online (Sandbox Code Playgroud)
它工作得很好(除了我无法阻止它,但这是一个不同的问题)。
接下来,我想测试我的ViewModel,其中包括完全相同的状态流。它基本上与上面的代码相同,但我认为它应该在相同的范围内运行,所以viewModel.someMutableStateFlow我尝试运行它viewModelScope:
@Test
fun debounce(): Unit = runBlocking {
val viewModel = MyViewModel()
// in view model the state and debouncedState are defined the same way as above
val state = …Run Code Online (Sandbox Code Playgroud) 我有一个状态流,一旦 HTTP 请求随响应返回,该状态流就会发出一个值,该值将在 Activity 中显示为列表,我使用 Kotlin 协程在StateFlowViewModel 和 Activity 之间进行通信。
我正在使用这样的androidx生命周期函数:repeatOnLifecycle
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.successFlow.collect { binding.recyclerView.adapter = ExampleAdapter(it) }
}
}
Run Code Online (Sandbox Code Playgroud)
一开始工作正常,但后来我意识到,每次用户转到另一个屏幕并返回前一个屏幕时,状态流都会重新发出该值,在这种情况下,该值将丢失列表状态,例如,如果用户滚动到10列表中的项目,然后转到另一个屏幕并返回,列表将滚动到位置,0因为setAdapter再次调用该方法,而使用 时则不是这种情况LiveData。
现在我也需要处理StateFlow状态和配置状态,我尝试使用该distinctUntilChanged方法,但正如文档所述Applying 'distinctUntilChanged' to StateFlow has no effect。
这里的问题是我如何LiveData使用StateFlow.
我需要处理流收集中的当前值和先前值,因此我需要一些具有如下作用的运算符:
----A----------B-------C-----|--->
---(null+A)---(A+B)---(B+C)--|--->
Run Code Online (Sandbox Code Playgroud)
一个想法是这样的:
fun <T: Any> Flow<T>.withPrevious(): Flow<Pair<T?, T>> = flow {
var prev: T? = null
this@withPrevious.collect {
emit(prev to it)
prev = it
}
}
Run Code Online (Sandbox Code Playgroud)
但这样就无法控制执行第一个流程的上下文。有没有更灵活的解决方案?
coroutine kotlin kotlin-coroutines coroutinescope kotlin-flow
kotlin ×10
kotlin-flow ×10
android ×2
android-jetpack-compose-list ×1
coroutine ×1
debounce ×1