我想使用 StateFlow。但目前,我找不到任何可以帮助我的讲座。
我面临一个问题:首先,我有一个包含字符串列表的单例,我想要一些“容易”理解的东西,即使它不是现在的目标目的。目的是用字符串填充并发出列表(稍后它将是一个复杂的对象)。
class CustomStateFlow() {
private val _custom = MutableStateFlow(emptyList<String>())
val custom: StateFlow<List<String>> = _custom
fun addString(string: String) {
val tempList = _custom.value.toMutableList()
tempList.add(string)
_custom.value = tempList
}
Run Code Online (Sandbox Code Playgroud)
这似乎有效,但我不喜欢临时列表...没有,我无法触发片段中自定义的“收集”。
有没有办法在不使用 tempList 的情况下实现这一目标?
感谢您
最近,我一直在使用 StateFlow、SharedFlow 和 Channels API,但在尝试将代码从 LiveData 迁移到表示层中的 StateFlow 时,我遇到了一个常见用例。
我面临的问题是,当我发出数据并将其收集到 viewModel 中时,我可以将值设置为 mutableStateFlow,当它最终到达片段时,它会使用 Toast 显示一些信息性消息,让用户知道是否发生错误发生了或者一切都很顺利。接下来,有一个按钮可以导航到另一个片段,但是如果我返回到已经有失败意图结果的上一个屏幕,它会再次显示 Toast。这正是我想要弄清楚的。如果我已经收集了结果并向用户显示了消息,我不想继续这样做。如果我导航到另一个屏幕并返回(当应用程序从后台返回时也会发生这种情况,它会再次收集最后一个值)。LiveData 没有发生这个问题,我只是做了完全相同的事情,公开来自存储库的流并通过 ViewModel 中的 LiveData 收集。
代码:
class SignInViewModel @Inject constructor(
private val doSignIn: SigninUseCase
) : ViewModel(){
private val _userResult = MutableStateFlow<Result<String>?>(null)
val userResult: StateFlow<Result<String>?> = _userResult.stateIn(viewModelScope, SharingStarted.Lazily, null) //Lazily since it's just one shot operation
fun authenticate(email: String, password: String) {
viewModelScope.launch {
doSignIn(LoginParams(email, password)).collect { result ->
Timber.e("I just received this $result in viewmodel")
_userResult.value = result
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的片段中: …
我遇到了以下问题:
这是注册屏幕,上面有几个输入字段。当用户输入内容时,该值会传递到 ViewModel,设置为屏幕状态并通过 StateFlow 传回屏幕。从可组合项中,我正在观察这个 StateFlow。问题是 Composable 在向 Flow 发出新值后并未失效。
这是视图模型代码:
class RegistrationViewModel : BaseViewModel() {
private val screenData = CreateAccountRegistrationScreenData.init()
private val _screenDataFlow = MutableStateFlow(screenData)
internal val screenDataFlow = _screenDataFlow.asStateFlow()
internal fun updateFirstName(name: String) {
screenData.firstName = name
updateScreenData()
}
private fun updateScreenData() {
viewModelScope.launch {
_screenDataFlow.emit(screenData.copy())
}
println()
}
}
Run Code Online (Sandbox Code Playgroud)
这是可组合的代码:
@Composable
fun RegistrationScreen(navController: NavController, stepName: String) {
val focusManager = LocalFocusManager.current
val viewModel: RegistrationViewModel by rememberInstance()
val screenData by viewModel.screenDataFlow.collectAsState()
Scaffold {
ConstraintLayout(
modifier = Modifier
.fillMaxSize() …Run Code Online (Sandbox Code Playgroud) 如果我们有两个这样定义的流:
val someflow = flow {
emit("something")
}
Run Code Online (Sandbox Code Playgroud)
另一个流程定义如下:
val stateFlow = MutableStateFlow("some value")
Run Code Online (Sandbox Code Playgroud)
是否可以将两个流组合成一个流,该流仅发出someflowor发出的最后一个值stateFlow?
这个想法是,stateFlow可能会在未来的某个时刻发出一个值,但在那之前我只想要someflow最后发出的任何值。在“组合”流程中,我想只获取 发出的第一个值someflow,但随后能够观察 上的其余更新stateFlow。
看起来这可以通过组合函数来完成,但我只想发出两个流之间的最新发出值,我不关心一个流的最后一个值是什么。
coroutine kotlin kotlin-coroutines kotlin-flow kotlin-stateflow
所以我正在更新我的RecylerView内容StateFlow<List>如下:
我的数据类:
data class Student(val name: String, var isSelected: Boolean)
Run Code Online (Sandbox Code Playgroud)
我的视图模型逻辑:
fun updateStudentsOnSelectionChanged(targetStudent: Student) {
val targetIndex = _students.value.indexOf(targetStudent)
val isSelected = !targetStudent.isSelected
_students.value[targetIndex].isSelected = isSelected //<- doesn't work
}
Run Code Online (Sandbox Code Playgroud)
问题: UI没变,但isSelected内部_student变了,这是怎么回事?(和...一样LiveData)
android kotlin android-livedata kotlin-flow kotlin-stateflow
Kotlin 1.4.21
Run Code Online (Sandbox Code Playgroud)
我有一个非常简单的 ViewModel,它使用协程和 stateFlow。但是,单元测试将失败,因为 stateFlow 似乎没有更新。
我认为这是因为测试将在stateFlow更新之前完成。
expected not to be empty
这是我正在测试的 ViewModel
class TrendingSearchViewModel @Inject constructor(
private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase,
private val coroutineDispatcher: CoroutineDispatcherProvider
) : ViewModel() {
private val trendingSearchMutableStateFlow = MutableStateFlow<List<String>>(emptyList())
val trendingSearchStateFlow = trendingSearchMutableStateFlow.asStateFlow()
fun getTrendingSearch() {
viewModelScope.launch(coroutineDispatcher.io()) {
try {
trendingSearchMutableStateFlow.value = loadTrendingSearchUseCase.execute()
} catch (exception: Exception) {
Timber.e(exception, "trending ${exception.localizedMessage}")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的实际测试课程,我尝试了不同的方法来使其正常工作
class TrendingSearchViewModelTest {
private val loadTrendingSearchUseCase: LoadTrendingSearchUseCase = mock()
private val coroutineDispatcherProvider = CoroutineDispatcherProviderImp()
private …Run Code Online (Sandbox Code Playgroud) android unit-testing kotlin kotlin-coroutines kotlin-stateflow
我尝试让用户选择是否使用 UI 模式“浅色”、“深色”或“系统”设置。我想将选择保存为数据存储。
用户选择的下拉菜单未从数据存储加载值。加载屏幕时,它始终显示 stateIn() 的初始值。
设置管理器:
val Context.dataStoreUiSettings: DataStore<Preferences> by preferencesDataStore(name = DATA_STORE_UI_NAME)
object PreferencesKeys {
val UI_MODE: Preferences.Key<Int> = intPreferencesKey("ui_mode")
}
class SettingsManager @Inject constructor(private val context: Context) { //private val dataStore: DataStore<Preferences>
private val TAG: String = "UserPreferencesRepo"
// Configuration.UI_MODE_NIGHT_UNDEFINED, Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_NO
suspend fun setUiMode(uiMode: Int) {
context.dataStoreUiSettings.edit { preferences ->
preferences[UI_MODE] = uiMode
}
}
fun getUiMode(key: Preferences.Key<Int>, default: Int): Flow<Int> {
return context.dataStoreUiSettings.data
.catch { exception ->
if (exception is IOException) {
Timber.i("Error reading preferences: …Run Code Online (Sandbox Code Playgroud) kotlin android-jetpack-compose android-jetpack-datastore kotlin-stateflow
最近,自 android 生命周期库版本 2.6.0-alpha01 以来,我们有了一个新的 API,即
\ncollectAsStateWithLifecycle(...)\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n如果您\xe2\x80\x99 使用 Jetpack Compose 构建 Android 应用程序,请使用\n
\ncollectAsStateWithLifecycle可组合函数(而不是\ncollectAsState)
我尝试一下,对于流动(冷流)例如
\n val counter = flow {\n var value = 0\n while (true) {\n emit(value++)\n delay(1000)\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n拥有它是有用的
\nflow.collectAsStateWithLifecycle(0)\nRun Code Online (Sandbox Code Playgroud)\n但是如果我们有一个像 mutableStateFlow 这样的热流
\nval stateFlow = MutableStateFlow(0)\nRun Code Online (Sandbox Code Playgroud)\n拥有似乎没什么用
\nstateFlow.collectAsStateWithLifecycle(0)\nRun Code Online (Sandbox Code Playgroud)\n鉴于它不会阻止任何排放。
\n我说collectAsStateWithLifecycle只对冷流有用,对热流无效,这样说对吗?
如果我错了,你能给我举一个collectAsStateWithLifecycle对热流也有用的例子吗?
android android-lifecycle kotlin kotlin-flow kotlin-stateflow
我有一个这样的密封类
sealed class LoadState {
class Loading : LoadState()
class Success : LoadState()
class Fail : LoadState()
}
Run Code Online (Sandbox Code Playgroud)
我将密封类与 LiveData 一起使用,它有效
open class BaseViewModel: ViewModel() {
// val loadState by lazy {
// MutableLiveData<LoadState>()
// }
val loadState by lazy {
MutableStateFlow(LoadState())
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我将 MutableLiveData 更改为 MutableStateFlow 时,我收到这样的警告
Sealed types cannot be instantiated
Run Code Online (Sandbox Code Playgroud)
那么,如何在 MutableStateFlow 中使用密封类呢?
我有一个状态流,它为我提供来自 ViewModel 的可变状态流的值,我想做的是,我想根据按钮单击显示隐藏 Web 视图。当值为 true 时,我想显示 Web 视图,当我想隐藏它时,我将其值更改为 false。这些值已正确更新,但未反映内部数据绑定。这是我的视图模型
class ArticleDetailsViewModel : ViewModel() {
private val _isWebViewShowing = MutableStateFlow(false)
val isWebViewShowing: StateFlow<Boolean>
get() = _isWebViewShowing
fun onReadMoreClicked() {
_isWebViewShowing.value = true
}
fun changeWebViewState() {
_isWebViewShowing.value = false
}}
Run Code Online (Sandbox Code Playgroud)
这是我正在进行比较的 XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="article"
type="io.infinity.newsapp.model.domain.ArticleDomainModel" />
<variable
name="viewModel"
type="io.infinity.newsapp.viewmodels.ArticleDetailsViewModel" />
<import type="android.view.View"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar_generic"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="@{viewModel.isWebViewShowing().value ? View.INVISIBLE :View.VISIBLE }"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/toolbar"
app:layout_constraintStart_toStartOf="@+id/toolbar" …Run Code Online (Sandbox Code Playgroud) android kotlin android-livedata kotlin-coroutines kotlin-stateflow
kotlin-stateflow ×10
android ×8
kotlin ×8
kotlin-flow ×4
coroutine ×1
sealed-class ×1
unit-testing ×1