Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sometimes, ConflatedBroadcastChannel fires recent value without any action #1980

Closed
theapache64 opened this issue Apr 30, 2020 · 2 comments
Closed
Assignees

Comments

@theapache64
Copy link

theapache64 commented Apr 30, 2020

In Google's official codelab about advanced-coroutines-codelab sample, they've used ConflatedBroadcastChannel to watch a variable/object change.

https://github.com/googlecodelabs/kotlin-coroutines/blob/b71b981f8354a92fb3ebda37eceb7461e783d0bf/advanced-coroutines-codelab/finished_code/src/main/java/com/example/android/advancedcoroutines/PlantListViewModel.kt#L91-L103

I've used same technique in one of my side projects, and when resuming the listening activity, sometimes ConflatedBroadcastChannel fires it's recent value, causing the execution of flatMapLatest body without any change.

I think this is happening while the system collects the garbage since I can reproduce this issue by calling System.gc() from another activity.

issue

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()

    }
}

Here's the full source code :
CoroutinesFlowTest.zip

Why is this happening? What am I missing?

@elizarov elizarov self-assigned this May 8, 2020
@elizarov
Copy link
Contributor

elizarov commented May 8, 2020

Thanks a lot for a detailed report! The problem here is that you are trying to use ConflatedBroadcastChannel for events, while it is designed to represent current state as shown in the codelab. Every time the downstream LiveData is reactivated it receives the most recent state and performs the incrementing action. Don't use ConflatedBroadcastChannel for events.

To fix it, you can replace ConflatedBroadcastChannel with BroadcastChannel<IncrementRequest>(1) (non-conflated channel, which is Ok for events to use) and it'll work as you expect it too.

Does it help?

@theapache64
Copy link
Author

theapache64 commented May 8, 2020

Yes. Absolutely. Thanks for responding @elizarov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants