我在我的项目中使用 MutableStateFlow。当我们初始化 MutableStateFlow 对象时,我们需要给出默认值。
val topics = MutableStateFlow<List<String>>(emptyList())
Run Code Online (Sandbox Code Playgroud)
当我收集这个值时
[null, "Hello", "world"]
Run Code Online (Sandbox Code Playgroud)
我想在适配器中传递这个列表。那么有没有一种方法可以在传入适配器之前删除空对象,或者有没有更好的方法?
viewModel.topics.collect { topicsList ->
println(topicsList) // [null, "Hello", "world"]
adapter.submitList(topicsList)
}
Run Code Online (Sandbox Code Playgroud) 我的视图模型中有两个状态流
private val _peopleList = MutableStateFlow(emptyList<People>())
val peopleList: StateFlow<List<People>> = _peopleList
val _peopleListLoader = MutableStateFlow(false)
val peopleListLoader: StateFlow<Boolean> = _peopleListLoader
Run Code Online (Sandbox Code Playgroud)
peopleList 用于显示列表,peopleListLoader 用于在 UI 中显示进度指示器。所有这些都按预期在应用程序中正常工作。但是在我的单元测试中,当我检查peopleListLoader使用peopleListLoader.toList(values)功能时,它没有我在人员列表加载期间分配给它的值。
以下是我的实现
人员列表视图模型
@HiltViewModel
class PeopleListViewModel @Inject constructor(
val repository: PeopleRepository,
@MainDispatcher private val dispatcher: CoroutineDispatcher
) : ViewModel() {
private val _peopleList = MutableStateFlow(emptyList<People>())
val peopleList: StateFlow<List<People>> = _peopleList
val _peopleListLoader = MutableStateFlow(false)
val peopleListLoader: StateFlow<Boolean> = _peopleListLoader
private val _peopleListErrorMessage = MutableStateFlow("")
var peopleListErrorMessage: StateFlow<String> = _peopleListErrorMessage
fun loadPeoplesList() { …Run Code Online (Sandbox Code Playgroud) 提供一些背景信息:我正在尝试测试一个内部有延迟的流程。为了很好地测试它,我想在某些时间断言值,因此我必须控制虚拟时间。然而,delay(...)似乎在测试中被跳过。举个例子:
@Test
fun test(dispatcher: TestDispatcher) = runTest(dispatcher) {
flow {
emit(1)
delay(10000)
emit(2)
}
.flowOn(dispatcher)
.test {
assertEquals(1, awaitItem())
assertEquals(2, awaitItem())
awaitComplete()
}
}
Run Code Online (Sandbox Code Playgroud)
在示例中,我预计会assertEquals(2, awaitItem())超时,因为advanceTimeBy(10001)没有被调用以超过延迟。然而,这个测试成功完成,所以它只是跳过了延迟。
有人知道这是为什么吗?我如何实现我想要的?
PSTestDispatcher 是一个JUnit5扩展提供的,在测试之前StandardTestDispatcher也会调用。流程上来自Turbine库。本示例使用的版本:Dispatchers.setMain.test { }
PPS 仅使用协程,行为就符合预期。以下面的例子为例。如果删除该advanceTimeBy测试将不再有效。
@Test
fun coroutineTest(dispatcher: TestDispatcher) = runTest(dispatcher) {
var number = 0
launch {
number = 1
delay(10_000)
number = 2
}
advanceTimeBy(10_001)
assertEquals(2, number)
}
Run Code Online (Sandbox Code Playgroud) 我想在现有的 Java 类中使用下面的代码,因为现在编写不可能在Kotlin中转换整个类:
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.tasksEvent.collect { event ->
}
}
Run Code Online (Sandbox Code Playgroud)
请建议最好的方法。我知道AsyncTask已被弃用。
我刚刚开始研究 Room、Coroutines 和 Flow,并且遇到了一些奇怪的事情:我期望的空流实际上有一个空项目。
我的设置如下,T对于我的实际实体是通用的。
interface TDao {
@Query("SELECT * FROM Table WHERE date=:date")
fun getT(date: String): Flow<T>
}
Run Code Online (Sandbox Code Playgroud)
@Singleton
class TRepository @Inject constructor(
private val apiService: TApiService,
private val Tdao: TDao
) {
suspend fun getTFor(date: String): Flow<T> =
Tdao
.getT(date)
.map {
if (it == null) {
returnTFromDatabase()
} else {
it
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当数据库中没有任何Tfor 时date,我希望它返回一个空流,其中没有任何项目。相反,它有一个null永远不会发生的元素,因为T它不可为空。
我为它写了这个测试:
@RunWith(AndroidJUnit4::class)
class TDatabaseTest {
private lateinit var db: TDatabase
private lateinit …Run Code Online (Sandbox Code Playgroud) 我使用 Room 和 Flows 制作了一个简单的示例应用程序:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val build = Room.databaseBuilder(this, FinanceDatabase::class.java, "database.db")
.fallbackToDestructiveMigration()
.build()
GlobalScope.launch {
build.currencyDao().addCurrency(CurrencyLocalEntity(1))
val toList = build.currencyDao().getAllCurrencies().toList()
Log.d("test", "list - $toList")
}
}
}
@Entity(tableName = "currency")
data class CurrencyLocalEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "currencyId")
var id: Int
) {
constructor() : this(-1)
}
@Dao
interface CurrencyDao {
@Query("SELECT * FROM currency")
fun getAllCurrencies(): Flow<CurrencyLocalEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addCurrency(currency: CurrencyLocalEntity)
}
@Database(entities …Run Code Online (Sandbox Code Playgroud) 我刚刚开始使用协程/流(以及一般的 kotlin),并且我正在努力将回调流转换为共享流。
我整理了下面的简单示例,只是为了展示我所尝试的内容,但没有成功。我的代码更复杂,但我相信这个示例反映了我想要实现的目标的问题。
fun main() = runBlocking {
getMySharedFlow().collect{
println("collector 1 value: $it")
}
getMySharedFlow().collect{
println("collector 2 value: $it")
}
}
val sharedFlow = MutableSharedFlow<Int>()
suspend fun getMySharedFlow(): SharedFlow<Int> {
println("inside sharedflow")
getMyCallbackFlow().collect{
println("emitting to sharedflow value: $it")
sharedFlow.emit(it)
}
return sharedFlow
}
fun getMyCallbackFlow(): Flow<Int> = callbackFlow<Int> {
println("inside callbackflow producer")
fetchSomethingContinuously {
println("fetched something")
offer(1)
offer(2)
offer(3)
}
awaitClose()
}
fun fetchSomethingContinuously(myCallBack: ()->Unit) {
println("fetching something...")
myCallBack()
}
Run Code Online (Sandbox Code Playgroud)
这个想法是fetchSomethingContinuously只被调用一次,与sharedFlow的收集器数量无关。但正如您从输出中看到的那样,收集器永远不会获取值:
inside sharedflow
inside callbackflow producer
fetching something... …Run Code Online (Sandbox Code Playgroud) 我有一个可能会抛出错误的流程,如下所示:
val myFlow = flow {
emit("1")
delay(2000)
emit("2")
delay(2000)
emit("3")
delay(2000)
emit("4")
delay(2000)
throw Exception() // here it would throw an error
delay(10000)
emit("6") // because the flow completes on error, it doesn't emit this
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,当抛出错误时,即使我添加.catch { error -> emit("5") }.. 它仍然完成流程,因此"6"不会发出。
myFlow.catch { error ->
emit("5")
}.onEach {
println("$it")
}.onCompletion {
println("Complete")
}.launchIn(scope)
Run Code Online (Sandbox Code Playgroud)
结果是:
1
2
3
4
5
Complete
Run Code Online (Sandbox Code Playgroud)
我需要它是:
1
2
3
4
5
6
Complete
Run Code Online (Sandbox Code Playgroud)
我想吞下错误而不是使流程完整。我怎样才能实现这个目标?
我有一个房间数据库中的剧集流。我可以毫无问题地将这个流程作为实时数据进行观察。
但我也想在用户单击按钮时读取此流程中的最后一个值。我尝试使用 first() 终端流运算符,但它无法编译。你能帮忙或提出其他建议吗?
从流中读取的非编译尝试:
bd.buttonNext.setOnClickListener {
lifecycleScope.launch {
val episode: Episode? = viewModel.episodeFlow().first() <=== Compile ERROR
Snackbar.make(bd.root, "episode ${episode?.name}", Snackbar.LENGTH_SHORT).show()
}
}
Run Code Online (Sandbox Code Playgroud)
此流程来自 ROOM :
@Query("SELECT * FROM Episode WHERE id = :id")
fun getEpisode(id: Long): Flow<Episode?>
Run Code Online (Sandbox Code Playgroud)
存储库:
fun getEpisode(episodeId: Long): Flow<Episode?> = dao.getEpisode(episodeId)
Run Code Online (Sandbox Code Playgroud)
视图模型 - Id 来自 StateFlow :
fun episodeFlow(): Flow<Episode?>
= episodeIdStateFlow.flatMapLatest { episodeId ->
repository.getEpisode(episodeId)
}
Run Code Online (Sandbox Code Playgroud)
编译错误:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun <T> Array<out …Run Code Online (Sandbox Code Playgroud) 在我的应用程序中,我使用 Kotlin Flow。之前我使用 挂起函数 EspressoIdlingResource.increment(),但它不适用于 Kotlin Flow。如何解决这个问题呢?