为什么在 kotlin 中流式收集调用超过两次?

why flow collect call more than twice in kotlin?

嘿,我在 android 从事 kotlin 流程工作。我注意到我的 kotlin 流程 collectLatest 调用了两次,有时甚至更多。我试过 但它对我不起作用。我在我的 collectLatest 函数中打印了日志,它打印了日志。我正在添加代码

MainActivity.kt

class MainActivity : AppCompatActivity(), CustomManager {

    private val viewModel by viewModels<ActivityViewModel>()
    private lateinit var binding: ActivityMainBinding
    private var time = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupView()
    }

    private fun setupView() {
        viewModel.fetchData()

        lifecycleScope.launchWhenStarted {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.conversationMutableStateFlow.collectLatest { data ->
                    Log.e("time", "${time++}")
                   ....
                }
            }
        }
    }
}

ActivityViewModel.kt

class ActivityViewModel(app: Application) : AndroidViewModel(app) {

    var conversationMutableStateFlow = MutableStateFlow<List<ConversationDate>>(emptyList())

    fun fetchData() {
        viewModelScope.launch {
            val response = ApiInterface.create().getResponse()
            conversationMutableStateFlow.value = response.items
        }
    }

   .....
}

我不明白为什么要调用两次。我正在附加日志

2022-01-17 22:02:15.369 8248-8248/com.example.fragmentexample E/time: 0
2022-01-17 22:02:15.629 8248-8248/com.example.fragmentexample E/time: 1

如你所见,它调用了两次。但是我加载的数据比它调用的多了两次。我不明白为什么它不止一次打电话。有人可以指导我做错了什么。如果您需要完整代码,我正在添加我的项目 link.

原因是collectLatest喜欢背压。如果您一次传递多个项目,流程将只收集最新的,但如果发射之间有一段时间,流程将像最新的一样收集每个项目

已编辑: 您真的需要了解 MVVM 架构。

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    setupView()
}

private fun setupView() {
    if (supportFragmentManager.findFragmentById(R.id.fragmentView) != null) 
        return
    
    supportFragmentManager
        .beginTransaction()
        .add(R.id.fragmentView, ConversationFragment())
        .commit()
}
}

删除 ActivityViewModel 并将该逻辑添加到 FragmentViewModel。 另请注意,如果可以使用普通 ViewModel,则不需要使用 AndroidViewModel。仅当您需要访问 Application 或其 Context

时才使用 AndroidViewModel

您正在使用 MutableStateFlow which derives from StateFlowStateFlow 具有初始值,您将其指定为 emptyList:

var conversationMutableStateFlow = MutableStateFlow<List<String>>(emptyList())

所以你第一次在 collectLatest 块中得到 data 时,它是一个空列表。第二次是响应中的列表。

当您调用 collectLatest 时,conversationMutableStateFlow 只有初始值,这是一个空列表,这就是您首先收到它的原因。

你可以把你的StateFlow改成SharedFlow,它没有初始值,所以你只会在collectLatest块中接到一个电话。在 ActivityViewModel class:

var conversationMutableStateFlow = MutableSharedFlow<List<String>>()

fun fetchData() {
    viewModelScope.launch {
        val response = ApiInterface.create().getResponse()
        conversationMutableStateFlow.emit(response.items)
    }
}

或者,如果您想坚持 StateFlow,您可以 filter 您的数据:

viewModel.conversationMutableStateFlow.filter { data ->
                data.isNotEmpty()
            }.collectLatest { data ->
                // ...
            }