繁体   English   中英

流不发射项目

[英]Flow not emitting items

下面的代码实现了一个简单的屏幕,显示了使用 MVI 模式和 kotlin 协程的用户列表。 代码运行但不产生任何结果。 经过大量调试,我发现UserListVM中的getUsers function 没有发出任何内容。 希望能得到一些帮助。 多谢。

    class UserListVM : ViewModel() {

    val resultFlows: Channel<Flow<*>> = Channel(Channel.UNLIMITED)
    val liveState = MutableLiveData<PModel<*, UserListIntents>>()
    val intents: Channel<UserListIntents> = Channel()

    lateinit var job: Job
    lateinit var currentState: UserListState

    fun offer(event: UserListIntents) = intents.offer(event)

    suspend fun store(initialState: UserListState): LiveData<PModel<*, UserListIntents>> {
        job = viewModelScope.launch {
            currentState = initialState
            intents.consumeEach { intent ->
                resultFlows.send(reduceIntentsToResults(intent, currentState)
                        .flowOn(Executors.newFixedThreadPool(4).asCoroutineDispatcher())
                        .map { SuccessResult(it, intent) }
                        .catch { ErrorEffectResult(it, intent) }
                        .onStart { emit(LoadingEffectResult(intent)) }
                        .distinctUntilChanged()
                )
            }
            resultFlows.consumeEach { results ->
                results.flatMapMerge {
                    val states = stateStream(this as Flow<Result<UserListResult, UserListIntents>>, currentState)
                    val effects = effectStream(this as Flow<Result<UserListEffect, UserListIntents>>)
                    flowOf(states, effects)
                }
                        .flattenMerge()
                        .collect { pModel -> liveState.value = pModel }
            }
        }
        job.start()
        return liveState
    }

    private suspend fun reduceIntentsToResults(intent: UserListIntents, currentState: Any): Flow<*> {
        Log.d("UserListVM", "currentStateBundle: $currentState")
        return when (intent) {
            is GetPaginatedUsersIntent -> when (currentState) {
                is EmptyState, is GetState -> getUsers()
                else -> throwIllegalStateException(intent)
            }
            is UserClickedIntent -> when (currentState) {
                is GetState -> flowOf((SuccessEffectResult(NavigateTo(intent.user), intent)))
                else -> throwIllegalStateException(intent)
            }
        }
    }

    private suspend fun getUsers(): Flow<UsersResult> {
        return flow {
            emit(UsersResult(listOf(User("user1", 1), User("user2", 2), User("user3", 3))))
        }.flowOn(Dispatchers.IO)
    }

    override fun onCleared() {
        super.onCleared()
        job.cancel()
    }
    }

    class UserListActivity : AppCompatActivity() {

    var intentStream: Flow<UserListIntents> = flowOf()
    lateinit var viewModel: UserListVM
    lateinit var viewState: UserListState
    private lateinit var usersAdapter: GenericRecyclerViewAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initialize()
        setupUI(savedInstanceState == null)
        viewModel.store(viewState).observe(this, Observer {
            it?.apply {
                when (this) {
                    is ErrorEffect -> bindError(errorMessage, error, intent)
                    is SuccessEffect -> bindEffect(bundle as UserListEffect)
                    is SuccessState -> {
                        (bundle as UserListState).also { state ->
                            viewState = state
                            bindState(state)
                        }
                    }
                }
                toggleLoadingViews(intent)
            }
        })
    }

    fun initialize() {
        viewModel = getViewModel()
        viewState = EmptyState()
    }

    fun setupUI(isNew: Boolean) {
        setContentView(R.layout.activity_user_list)
        setSupportActionBar(toolbar)
        toolbar.title = title
        setupRecyclerView()
    }

    override fun onResume() {
        super.onResume()
        if (viewState is EmptyState) {
            GlobalScope.launch {
                viewModel.offer(GetPaginatedUsersIntent(0))
            }
        }
    }

    private fun bindState(successState: UserListState) {
        usersAdapter.setDataList(successState.list, successState.callback)
    }

    private fun bindEffect(effectBundle: UserListEffect) {
        when (effectBundle) {
            is NavigateTo -> {
               // ..
            }
        }
    }

    fun bindError(errorMessage: String, cause: Throwable, intent: UserListIntents) {
        //..
    }

    private fun setupRecyclerView() {
        usersAdapter = object : GenericRecyclerViewAdapter() {
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder<*> {
                return when (viewType) {
                    R.layout.empty_view -> EmptyViewHolder(layoutInflater
                            .inflate(R.layout.empty_view, parent, false))
                    R.layout.user_item_layout -> UserViewHolder(layoutInflater
                            .inflate(R.layout.user_item_layout, parent, false))
                    else -> throw IllegalArgumentException("Could not find view of type $viewType")
                }
            }
        }
        usersAdapter.setAreItemsClickable(true)
        user_list.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this)
        user_list.adapter = usersAdapter
        usersAdapter.setAllowSelection(true)
        intentStream = flowOf(intentStream, user_list.scrollEvents()
                .map { recyclerViewScrollEvent ->
                    GetPaginatedUsersIntent(
                            if (ScrollEventCalculator.isAtScrollEnd(recyclerViewScrollEvent))
                                viewState.lastId
                            else -1)
                }
                .filter { it.lastId != -1L }
                .conflate()
                .onEach { Log.d("NextPageIntent", "fired!") })
                .flattenMerge()
    }

    fun toggleLoadingViews(isLoading: Boolean, intent: UserListIntents?) {
        linear_layout_loader.bringToFront()
        linear_layout_loader.visibility = if (isLoading) View.VISIBLE else View.GONE
    }
    }

我认为这里的问题是intents.consumeEach正在等待intents关闭,但它从未关闭。 这意味着永远不会达到resultFlows.consumeEach ,因此看起来getUsers没有发出任何东西,但实际上并没有被消耗。

快速解决方法是将每个consumeEach包装在一个launch中,但我建议进行重构/重新设计。

旁注:返回Flow的函数不应挂起。

在我的具体情况下, distinctUntilChanged是未收到某些值的原因

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM