我有一个设置,其中一个活动包含两个片段(a,B),B有一个包含3个片段(B1,B2,B3)的ViewPager

在活动(ViewModel)中,我从房间中观察模型(Model),并将结果发布到本地共享流.

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

片段A和B(视图模型)可以通过(父)fun getModelFlow(): Flow<Model>访问sharedFlow

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

但是,问题在于嵌套片段(B1等)

我在获取流(即SharedFlow(作为活动视图模型中的流))方面没有问题;但B1中的collect没有任何作用.

  • 为什么我不能从嵌套B1中的共享流中收集数据?(A和B工作正常时)
  • 我是否已经没有考虑一些流量规则了?额外launch{}'es,其他withContexts(Some?)flowOnlaunchIn等.?

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

=== EDIT ===

我被要求添加更多信息,不幸的是(尽管如此),我无法粘贴实际的代码,因为它只会显得冗长和陌生(见我下面的 comments ).但这里有一些psuedo代码应该是等效的.

下面是您可以看到的一个说明: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")
            }
        }
    }
}

因此,获取parentViewModel、DI、片段创建等所有连接都工作得很好.但是B1 model: $model从来没有叫过!为什么?

推荐答案

这与碎片、生命周期、流和协同 routine 阻塞几乎没有(读不到)联系-我认为这是背后的原因.

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,但我不想麻烦初始状态/值)

Android相关问答推荐

Android Compose Lazy柱形实时UI更改

在模块中找到重复的类com.google.Firebase.auth.ktx.AuthKt||Android Studio

Jetpack创作动画断断续续变化的观点

Android系统应用程序启用编程以太网网络共享

使用 Gadle kotlin 为多模块 Android 代码库设置 jacoco

使用 settings.gradle 文件将 Firebase 依赖项添加到 Android 项目

为什么@PrimaryKey val id: Int? = null 在创建 Room 实体时有效吗?

在段的中心绘制饼图(甜甜圈图)的图例

如何在 Jetpack Compose 中创建无限pager

我该怎么做文本计时器

在 Kotlin 中打开新片段时如何对当前片段应用更改?

为 AlertDialog 的消息文本设置自定义字体

如何将设备屏幕位置转换为发送事件位置?

如何使用底页,启用和展开父交互

为什么官方文档用大写字母表示val变量?

优化 Room 数据库迁移

如何授予对 Uri 图像的永久权限(androidx)

Android Studio,Db 连接错误:发生异常情况导致驱动程序失败.请报告此异常

ObjectBox,如何在冲突中放弃一切迁移?

如何优化 JetPack Compose 列表性能?