所以我有一个 ViewModel 我正在尝试进行单元测试。它使用 stateIn 运算符。我找到了有关如何测试使用 stateIn 运算符创建的状态流的文档https://developer.android.com/kotlin/flow/test但即使我正在收集流,mapLatest也永远不会触发。
\nclass DeviceConfigurationViewModel(\n val systemDetails: SystemDetails,\n val step: AddDeviceStep.ConfigureDeviceStep,\n val service: DeviceRemoteService\n) : ViewModel(), DeviceConfigurationModel {\n\n @OptIn(ExperimentalCoroutinesApi::class)\n private val _state: StateFlow<DeviceConfigurationModel.State> =\n service.state\n .mapLatest { state ->\n when (state) {\n DeviceRemoteService.State.Connecting -> {\n DeviceConfigurationModel.State.Connecting\n }\n is DeviceRemoteService.State.ConnectedState.Connected -> {\n state.sendCommand(step.toCommand(systemDetails))\n DeviceConfigurationModel.State.Connected\n }\n is DeviceRemoteService.State.ConnectedState.CommandSent -> {\n DeviceConfigurationModel.State.Configuring\n }\n is DeviceRemoteService.State.ConnectedState.MessageReceived -> {\n transformMessage(state)\n }\n is DeviceRemoteService.State.Disconnected -> {\n transformDisconnected(state)\n }\n }\n }\n .distinctUntilChanged()\n .stateIn(\n viewModelScope,\n SharingStarted.WhileSubscribed(5000), // Keep it alive for …Run Code Online (Sandbox Code Playgroud) android kotlin android-viewmodel kotlin-coroutines kotlin-flow
这SoundViewModel是一个ViewModel类,val listSoundRecordState可能被应用程序中的某些模块使用。
在代码 A 中,我fun collectListSoundRecord()在需要使用数据时调用listSoundRecordState。但fun collectListSoundRecord()可能会因为Jetpack Compose重新组合而反复启动,不知道会不会耗费很多系统资源?
在代码B中,我 private fun collectListSoundRecord()在 中启动init { },collectListSoundRecord()只会启动一次,但即使我不需要使用数据listSoundRecordState,它也会保留在内存中直到App代码关闭,这种方式会消耗很多系统资源吗?
代码A
@HiltViewModel
class SoundViewModel @Inject constructor(
...
): ViewModel() {
private val _listSoundRecordState = MutableStateFlow<Result<List<MRecord>>>(Result.Loading)
val listSoundRecordState = _listSoundRecordState.asStateFlow()
init { }
//It may be launched again and again
fun collectListSoundRecord(){
viewModelScope.launch {
listRecord().collect {
result -> _listSoundRecordState.value =result
}
}
}
private fun listRecord(): Flow<Result<List<MRecord>>> …Run Code Online (Sandbox Code Playgroud) kotlin kotlin-coroutines android-jetpack-compose kotlin-flow
EventHandler.sharedFlow单击按钮时会发出热流。
Repository该流由在 中执行某些操作接收OnEach{}。
EventCollectorA然后,两个事件收集器和接收存储库流EventCollectorB。
然后,事件收集器流被组合并收集在 中MyViewModel。
这两个事件收集器会导致onEach{...}每次单击时运行两次。但是我只想运行onEach{...}一次并在两个事件收集器中接收它。我怎样才能实现这个目标?
注意:我使用 Hilt 只拥有一个Repository,EventCollectorA实例EventCollectorB
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
binding.buttonB.setOnClickListener {
viewModel.userClickEvent("Click Event")
}
}
}
Run Code Online (Sandbox Code Playgroud)
@HiltViewModel
class MyViewModel @Inject constructor(
private val eventHandler: EventHandler,
private val eventCollectorA: EventCollectorA,
private val eventCollectorB: EventCollectorB,
) : ViewModel() { …Run Code Online (Sandbox Code Playgroud) 如果代码中发生某些情况,我想取消 kotlin 流程。
假设我有一个方法如下
fun test(): Flow<String> = flow {
val lst = listOf("A", "B", "C")
while(true) {
lst.forEach { emit(it) }
//If some condition occurs, need to return from here, else continue
//How to stop flow here
}
}
Run Code Online (Sandbox Code Playgroud)
并称其为
test().collect { println(it)}
Run Code Online (Sandbox Code Playgroud)
问题是,如何停止流在某些条件下(来自流生成器或流生成器之外)产生任何内容?
asynchronous reactive-programming kotlin rx-java kotlin-flow
我注意到很多人和例子使用 Flows 作为 List<> 的包装器,例如这样:
@Query("SELECT * from some_model ORDER BY some_field")
fun getData(): Flow<List<some_model>>
Run Code Online (Sandbox Code Playgroud)
据我所知,Flow 就像一种“异步序列”,所以我真的不明白“Flow<List<T>>”的含义。
为什么我们不能直接使用 Flow< T > ,这对我来说似乎很直观,我问这个问题是因为我在代码片段中看到它重复了很多次并且无法理解其背后的目的?
我们目前如何从 swift收集Flow?
通过在 Swift流的collect方法中对 Kotlin 的挂起函数的新支持,可以在 swift 中获得这个相当笨重的签名
vm.topStoriesFlow.collect(
collector: Kotlinx_coroutines_coreFlowCollector,
completionHandler: @escaping (KotlinUnit?, Error?) -> Void
)
Run Code Online (Sandbox Code Playgroud)
知道如何使用它,或者即使当前支持它的使用吗?
kotlin kotlin-native kotlin-multiplatform kotlin-coroutines kotlin-flow
我的问题实际上很笼统。我想知道如何对从Paging 3返回PagingSource的Room Dao查询进行单元测试。
我有一个Room Dao查询:
@Query("SELECT * FROM database")
fun getChocolateListData(): PagingSource<Int, Chocolate>
Run Code Online (Sandbox Code Playgroud)
我想知道如何对这个查询进行单元测试。
到目前为止我尝试过的(使用内存中的Room数据库进行测试):
@FlowPreview
@Test
fun saveChocolateToDbSavesData() = runBlocking {
val dao: Dao by inject()
val chocolate = Chocolate(
name = "Dove"
)
dao.saveChocolate(chocolate)
val pagingSourceFactory = { dao.getChocolateListData() }
val pagingDataFlow: Flow<PagingData<Chocolate>> = Pager(
config = PagingConfig(
pageSize = 50,
maxSize = 200,
enablePlaceholders = false
),
pagingSourceFactory = pagingSourceFactory
).flow
val chocolateListFlow = pagingDataFlow.testIn(coroutinesTestRule)
Assert.assertEquals(PagingData.from(listOf(chocolate)), chocolateListFlow.emissions[0]) …Run Code Online (Sandbox Code Playgroud) android android-room kotlin-flow android-unit-testing android-paging-3
我正在像这样创建 MutableStateFlow:
val intSet = MutableStateFlow(HashSet<Int>())
Run Code Online (Sandbox Code Playgroud)
稍后我想更新此流程中的集合:
intSet.value.add(0)
Run Code Online (Sandbox Code Playgroud)
这似乎不起作用(集合更新,但观察者没有收到通知)。我发现它的工作方式:
val list = HashSet<Int>(intSet.value)
list.add(0)
intSet.value = list
Run Code Online (Sandbox Code Playgroud)
但它创建了该集合的副本,因此它看起来不适合我。有没有更简单的方法来更新 StateFlow 中的集合?
当我在不同的片段中更改 GameData 的 isInFavorites 属性时,我可以看到在我的存储库的侦听器中收到了更改,但是当我导航回片段时,当我使用 MutableStateFlow 时,我的视图模型永远不会收到更新的值。
奇怪的是,当我将流更改为 MutableSharedFlow 时,突然间视图模型也开始获取更新的值。有谁知道为什么会发生这种情况?我需要在这里使用 MutableStateFlow 但它不起作用。
存储库:
private val gameDataListResultMutableFlow: MutableStateFlow<Result<List<GameData>>> = MutableStateFlow(Result.Loading)
override suspend fun observeGameDataList(): Flow<Result<List<GameData>>>
{
CoroutineScope(Dispatchers.IO + coroutineContext).launch {
localGameDataSource.observeGameDataList().collectLatest{
if(it is Result.Success)
{
Timber.d("local data change favorite value of item 0: ${it.data[0].isInFavorites}")
}
gameDataListResultMutableFlow.emit(it)
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型:
private suspend fun observeGameListResult()
{
gameRepository.observeGameDataList().collect{
if(it is Result.Success)
Timber.d("data change received in viewmodel value of item 0: ${it.data[0].isInFavorites}")
gameListResultMutableLiveData.postValue(it)
}
}
fun getGameListResultLiveData(): LiveData<Result<List<GameData>>>
{
launch(coroutineContext) {
observeGameListResult()
} …Run Code Online (Sandbox Code Playgroud) 我在两个片段之间共享 MutableStateFlow 属性时遇到问题。
为了使其易于理解:
BasicViewModel由于导航图的实现,我有一个应该始终是两个片段的一个实例
private val basicViewModel: basicViewModel by navGraphViewModels(R.id.basic_graph) { defaultViewModelProviderFactory }
Run Code Online (Sandbox Code Playgroud)
这个 ViewModel 有一个MutableStateFlow像这样声明的属性
private val _basicProperty = MutableStateFlow<BasicClass?>(null)
val basicProperty : Flow<BasicClass?> = _basicId
.filterNotNull()
.flatMapConcat { someRepository.getBasicProperty(it) }
.onEach { _basicProperty.value = it }
.catch { }
Run Code Online (Sandbox Code Playgroud)
然后,我使用导航图在导航中FragmentA声明FragmentB,该导航图类似地调用该属性,如下所示
basicViewModel.basicProperty
.filterNotNull()
.mapNotNull { it.innerProperty}
.onEach { doSomething(it) }
.launchIn(viewLifecycleOwner.lifecycleScope)
Run Code Online (Sandbox Code Playgroud)
一切看起来都很好,但是当我导航到FragmentABasicProperty 加载流程(从 WebApi 加载数据)时,我导航到FragmentB 并再次加载流程,而不是调用已加载的数据,在 App 中,由于重新加载,它看起来有点滞后
问题:我应该做什么/更改才能从BasicViewModelin 中获取已有的数据FragmentB?
kotlin-flow ×10
kotlin ×9
android ×5
android-room ×1
asynchronous ×1
collections ×1
java ×1
navigation ×1
rx-java ×1
viewmodel ×1