标签: kotlin-flow

如何避免 MutableStateFlow kotlin 中的默认值

我在我的项目中使用 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)

android kotlin kotlin-flow kotlin-stateflow

5
推荐指数
1
解决办法
7499
查看次数

viewmodel 中的单元测试状态流 - stateflow 只有初始值,后面分配的值不会在单元测试中返回

我的视图模型中有两个状态流

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)

android unit-testing kotlin-coroutines kotlin-flow

5
推荐指数
0
解决办法
1054
查看次数

为什么测试中的 Kotlin Flow 会跳过延迟?

提供一些背景信息:我正在尝试测试一个内部有延迟的流程。为了很好地测试它,我想在某些时间断言值,因此我必须控制虚拟时间。然而,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 { }

  • 科特林:1.6.21
  • Kotlin 协程:1.6.1
  • 涡轮机:0.8.0
  • JUnit5:5.8.2

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)

unit-testing kotlin kotlin-coroutines kotlin-flow

5
推荐指数
1
解决办法
1636
查看次数

为什么我在 Java Android 中没有获得生命周期作用域?

我想在现有的 Java 类中使用下面的代码,因为现在编写不可能在Kotlin中转换整个类:

viewLifecycleOwner.lifecycleScope.launchWhenStarted {
    viewModel.tasksEvent.collect { event ->
            
    }
}
Run Code Online (Sandbox Code Playgroud)

请建议最好的方法。我知道AsyncTask已被弃用。

java android kotlin kotlin-coroutines kotlin-flow

5
推荐指数
2
解决办法
2316
查看次数

Room with Flow 在空时返回 null

我刚刚开始研究 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)

android kotlin android-room kotlin-coroutines kotlin-flow

4
推荐指数
2
解决办法
3153
查看次数

带有 Kotlin Flow toList() 的 Android Room 库不起作用

我使用 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)

android android-room kotlin-coroutines kotlin-flow

4
推荐指数
1
解决办法
2229
查看次数

将回调流转换为共享流

我刚刚开始使用协程/流(以及一般的 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)

kotlin kotlin-coroutines kotlin-flow

4
推荐指数
1
解决办法
5335
查看次数

如何抑制协程流程中的错误,使流程无法完成?

我有一个可能会抛出错误的流程,如下所示:

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)

我想吞下错误而不是使流程完整。我怎样才能实现这个目标?

kotlin kotlin-coroutines kotlin-flow

4
推荐指数
1
解决办法
6354
查看次数

Android - 如何从 kotlin 流程中读取值?

我有一个房间数据库中的剧集流。我可以毫无问题地将这个流程作为实时数据进行观察。

但我也想在用户单击按钮时读取此流程中的最后一个值。我尝试使用 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)

android kotlin-flow kotlin-coroutines-flow kotlin-stateflow

4
推荐指数
1
解决办法
1028
查看次数

如何将 Espresso 与 Kotlin Flow 结合使用?

在我的应用程序中,我使用 Kotlin Flow。之前我使用 挂起函数 EspressoIdlingResource.increment(),但它不适用于 Kotlin Flow。如何解决这个问题呢?

android-testing android-espresso kotlin-flow

4
推荐指数
1
解决办法
1119
查看次数