为什么不调用嵌套片段 (ViewModel) 中流的收集?

Why is the collect of a flow in a nested fragment (ViewModel) not called?

我有一个设置,其中 Activity 包含两个片段(A、B),B 有一个包含 3 个片段(B1、B2、B3)的 ViewPager

在 activity (ViewModel) 中,我从 Room 观察模型 (Model),并将结果发布到本地共享流。

val mSharedFlow = MutableSharedFlow<Model>` that I publish the model updates to: 
...
viewModelScope.launch { repo.observeModel().collect(sharedFlow) }

片段 A 和 B(ViewModel)可以通过(父级)访问 sharedFlow fun getModelFlow(): Flow<Model> 没有问题 运行 每个片段的收集:

viewModelScope.launch { 
  parent.getModelFlow().collect { model -> doStuff(model) }
}

但是,问题出在嵌套片段中(B1 等) 在 B1 的片段 (ViewModel) 中,我有另一个 parent.getModelFlow() 依次调用片段 B (ViewModel) parent.getParentFlow()

我在获取流时没有问题(即 SharedFlow(来自 activity ViewModel 的流));但是 B1 中的 collect 什么都不做。

(提供流不是问题。即使我创建中间流,或将 sharedFlow 放在 kotlin Singleton 对象中,我仍然有同样的问题)

=== 编辑 ===

我被要求添加更多信息,不幸的是(对于所有人)我无法粘贴实际代码,因为它只会显得冗长和陌生(请参阅下面我的评论)。但是这里有一些应该等效的伪代码。

一个澄清,你可以在下面看到,Activity,FragmentA、FragmentB、FragmentB1等同时都是运行——但只有A和B之一可见一次。


class TheActivity {
    fun onCreate() {
        setupFragments()
    }

    /** Two fragments active at the same time,
    but only one FrameLayout is visible at one time */
    private fun setupFragments() {
        val a = FragmentA.newInstance()
        val b = FragmentB.newInstance()

        supportFragmentManager.commit {
            add(R.id.fragment_holder_a, a)
            add(R.id.fragment_holder_b, b)
        }
    }
}

class ActivityViewModel {
    val activityModelFlow = MutableSharedFlow<Model>()

    fun start() {
        viewModelScope.launch {
            getRoomFlow(id).collect(activityModelFlow)
        }
    }
}

class FragmentA { // Not very interesting
    val viewModel: ViewModelA
}

class ViewModelA {
    fun start() {
        viewModelScope.launch {
            parentViewModel.activityModelFlow.collect { model ->
                log("A model: $model")
            }
        }
    }
}

class FragmentB {
    val viewModel: ViewModelB
    val viewPagerAdapter = object : PagesAdapter.Pages {
        override val count: Int = 1
        override fun title(position: Int): String = "B1"
        override fun render(position: Int): Fragment = FragmentB1.newInstance()
    }
}

class ViewModelB {
    val bModelFlow: Flow<Model> get() = parentViewModel.activityModelFlow
    fun start() {
        viewModelScope.launch {
            parentViewModel.activityModelFlow.collect { model ->
                log("B model: $model")
            }
        }
    }
}

class Fragment B1 {
    val viewModel: ViewModelB1
}

class ViewModelB1 {
    fun start() {
        viewModelScope.launch {
            // in essence: 
            // - ViewModelB.bModelFlow -> 
            // - ActivityViewModel.modelFlow
            parentViewModel.bModelFlow.collect { model ->
                log("B1 model: $model")
            }
        }
    }
}

因此,获取 parentViewModels、DI、片段创建等所有连接都工作正常。但是 B1 model: $model 永远不会被调用!为什么?

这与片段、生命周期、流程和协程阻塞几乎没有关联(读作没有)——我认为这是背后的原因。

val activityModelFlow = MutableSharedFlow<Model>()
// is the same as 
val activityModelFlow = MutableSharedFlow<Model>(
    replay = 0,
    extraBufferCapacity = 0,
    onBufferOverflow = BufferOverflow.SUSPEND
)

这意味着新订阅者将可以访问存储在重播缓存中的 0(!) 个值。因此,当 FragmentB1 开始订阅时,模型已经发出。

解决方案(没有任何优化或进一步考虑)

private val bulletFlow = MutableSharedFlow<Bullet>(replay = 1)

(或使用 StateFlow,但我不想打扰初始 state/value)