ConflatedBroadcastChannel

ConflatedBroadcastChannel

在Google的官方代码实验室中,有关 advanced-coroutines-codelab 的示例中,他们将ConflatedBroadcastChannel用作watch a variable/object change

我在一个辅助项目中使用了相同的技术,并且在恢复监听 Activity 时,有时ConflatedBroadcastChannel会触发其最近的值,从而导致flatMapLatest主体的执行没有任何更改。

我认为这是在系统收集垃圾时发生的,因为我可以通过从另一个 Activity 中调用System.gc()来重现此问题。

这是代码

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        val tvCount = findViewById<TextView>(R.id.tv_count)

        viewModel.count.observe(this, Observer {
            tvCount.text = it
            Toast.makeText(this, "Incremented", Toast.LENGTH_LONG).show();
        })

        findViewById<Button>(R.id.b_inc).setOnClickListener {
            viewModel.increment()
        }

        findViewById<Button>(R.id.b_detail).setOnClickListener {
            startActivity(Intent(this, DetailActivity::class.java))
        }

    }
}

MainViewModel.kt

class MainViewModel : ViewModel() {

    companion object {
        val TAG = MainViewModel::class.java.simpleName
    }

    class IncrementRequest

    private var tempCount = 0
    private val requestChannel = ConflatedBroadcastChannel<IncrementRequest>()

    val count = requestChannel
        .asFlow()
        .flatMapLatest {
            tempCount++
            Log.d(TAG, "Incrementing number to $tempCount")
            flowOf("Number is $tempCount")
        }
        .asLiveData()

    fun increment() {
        requestChannel.offer(IncrementRequest())
    }
}

DetailActivity.kt

class DetailActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detail)
        val button = findViewById<Button>(R.id.b_gc)


        val timer = object : CountDownTimer(5000, 1000) {
            override fun onFinish() {
                button.isEnabled = true
                button.text = "CALL SYSTEM.GC() AND CLOSE ACTIVITY"
            }

            override fun onTick(millisUntilFinished: Long) {
                button.text = "${TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished)} second(s)"
            }
        }

        button.setOnClickListener {
            System.gc()
            finish()
        }

        timer.start()

    }
}

这是完整的源代码:
CoroutinesFlowTest.zip
  • 为什么会这样?
  • 我想念什么?
  • 最佳答案

    除了answer of Kiskae:

    这可能不是您的情况,但是您可以尝试在接收方使用BroadcastChannel(1).asFlow().conflate,但就我而言,这导致了一个错误,即有时未触发接收方的代码(我认为是因为conflate在单独的环境中工作协程或其他东西)。

    或者,您可以使用无状态ConflatedBroadcastChannel的自定义版本(位于here)。

    class StatelessBroadcastChannel<T> constructor(
        private val broadcast: BroadcastChannel<T> = ConflatedBroadcastChannel()
    ) : BroadcastChannel<T> by broadcast {
    
        override fun openSubscription(): ReceiveChannel<T> = broadcast
            .openSubscription()
            .apply { poll() }
    
    }
    

    07-28 03:49