Hi guys, I am doing baby step on `Channel` Buffer....
# coroutines
k
Hi guys, I am doing baby step on
Channel
Buffer. I am learning to Poll item through
Channel
. When I send item, it doesn't
receive()
all item. I don't understand why?
Copy code
class QueueViewModel(private val application: Application) : AndroidViewModel(application) {


    val basketChannel = Channel<String>(Channel.UNLIMITED)
    
    init {
        startPolling()
    }

 
    fun addItems() {
        addItemInChannel(100L, "Item 1")
        addItemInChannel(1000L, "Item 2")
        addItemInChannel(400L, "Item 3")
        addItemInChannel(500L, "Item 4")
    }

    fun addItemInChannel(delay: Long, item: String) {
        viewModelScope.launch {
            delay(delay)
            logE("basketChannelItem added -> $item")
            basketChannel.send(item)
        }
    }

    fun startPolling() {
        viewModelScope.launch {
            Log.e(TAG, "Starting Polling")
            for (element in basketChannel) {
                logE("basketChannel Item poll -> $element")
                basketChannel.receive()
            }
        }
    }
}
I called
addItems()
in activity..
Screenshot 2023-02-08 at 16.36.18.png
c
You don’t explicitly “poll” a Channel. Simply using its Iterator
for element in basketChannel
will react once an item is sent to the channel. It’s a push-based mechanism, where items that are sent to the channel cause the receiver to react to the item, as opposed to a “poll” which is a pull-based mechanism The additional
basketChannel.receive
will cause the channel to consume an additional item, effectively consuming 2 items for every 1 iteration, basically dropping every other item in your case (or suspending indefinitely if you do not send an even number of items)
j
remove the receive call from the startPolling method
k
So How can I use pull base mechanism. Actually I want to queue the Item, if something is processng.
j
the next element won't be removed from the queue until a single iteration of that
for
body is complete.
Thats what the
for in
does. Removes 1 element from the queue processes it (in this case just logs it) then removes the next element. It will keep doing that until something closes the channel. if the channel is empty it will suspend until something is added
k
Okk, then I'll remove the
for
. from the startPolling
c
When someone calls
channel.send()
or
channel.trySend()
, an item gets placed into a queue within the channel. That item will stay within that queue until somewhere else, something starts reading from the channel. There are several ways to read values from the channel: You can implement your own loop and manually call
receive()
within the loop:
Copy code
while(true) {
    val item = channel.receive()
    handleItem(item) 
}
You can use the Channels Iterator
Copy code
for(item in channel) {
    handleItem(item) 
}
You can receive the items as a Flow:
Copy code
channel
    .receiveAsFlow()
    .onEach { handleItem(it) }
    .launchIn(viewModelScope)
All of these mechanisms effectively do the same thing: read items one at a time from the Channel’s queue, suspending until new items are sent. However, they’re not perfectly equivalent. Most notably, the first example (directly calling
channel.receive()
) will throw an exception once the channel is closed, while the other two mechanisms (iterator and Flow), will simply stop sending items without throwing an exception. This is why we’re recommending using the Iterator rather than calling
channel.receive()
, because it’s more natural to how you would typically use a Channel and you don’t need to worry about handling exceptions
Try not to get stuck trying to make Kotlin stuff look the same as Java. It’s got great compatibility with Java, but it’s not simply re-implementing the old Java ways of doing things. Kotlin is its own language, with its own ways of doing things which are quite different from Java in many aspects, especially when it comes to coroutines and concurrency Instead, rather than trying to recreate the Java Queue API in Kotlin, try to figure out the Kotlin way to do it. This means not using
channel.receive()
manually, and using the iterator instead. And more generally, try to understand the ways things are done in Kotlin first, before comparing the Kotlin APIs to Java ones
k
Thank you so much for great explanation. I'll keep in mind what you just mention.