https://kotlinlang.org logo
Join the conversationJoin Slack
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by Linen
coroutines
  • p

    Pitel

    05/16/2022, 10:43 AM
    I have code that basicaly looks like this:
    someFlow.collect {
      Log.d(TAG, ">")
      // Some stuff
      Log.d(TAG, "<")
    }
    So, I should in Android's logcat alternating arrows going left and right. And in most cases, I do. But on some rare occasions, I see 2 going right and 2 going left. Any idea why that might happen?!
    m
    b
    y
    • 4
    • 11
  • b

    Brian Estrada

    05/17/2022, 7:14 AM
    I have a question, I’m trying to replace promises with coroutines but I can’t figure out a way to create a
    Deferred<Int>
    and then call it and complete it later by another function (Will add an example in a 🧵)
    s
    s
    • 3
    • 5
  • g

    ghosalmartin

    05/17/2022, 9:59 AM
    I have an actor, that delivers messages to 4 sub actors. How can I ensure synchronisation? Why do I have sub actors? To avoid deadlocking when needing to call a different internal method, This is the code is anyones curious https://github.com/ghosalmartin/AnySubscripts/blob/master/src/main/kotlin/Store.kt Thanks
    a
    c
    • 3
    • 15
  • s

    Sanat Tripathi

    05/17/2022, 8:45 PM
    Hello folks, I have a question regarding
    suspendCancellableCoroutine
    and
    Mutex
    suspend fun awaitCallback(): T = suspendCancellableCoroutine { continuation ->
    
        val callback = object : Callback { // Implementation of some callback interface
            override fun onCompleted(value: T) {
                // Resume coroutine with a value provided by the callback
                continuation.resume(value)
            }
            override fun onApiError(cause: Throwable) {
                // Resume coroutine with an exception provided by the callback
                continuation.resumeWithException(cause)
            }
        }
    
        // Register callback with an API
        api.register(callback)
        // Remove callback on cancellation
        continuation.invokeOnCancellation { api.unregister(callback) }
        // At this point the coroutine is suspended by suspendCancellableCoroutine until callback fires
    }
    
    suspend fun execute() {
        val mutex = Mutex(false)
        
        mutex.lock()
     
     	try {
       		awaitCallback()    
        } catch(e: Throwable) {
            throw e
        } finally {
            mutex.unlock()
        }
     
    }
    In the above setup, the moment suspension happens at
    suspendCancellableCoroutine
    mutex in
    execute
    method gets unlocked part of the
    finally
    block
    m
    n
    u
    • 4
    • 9
  • n

    Nino

    05/18/2022, 2:57 PM
    Hello ! Is there a difference (except style) between the "first" and "second" way to associate the 3 flows? If yes, what is the preferred way, performance-wise ? To me, the second way is preferrable only because it's easier to read. But maybe the first one is more performant ?
    import kotlinx.coroutines.*
    import kotlinx.coroutines.flow.*
    
    fun main() = runBlocking {
        val flowA : Flow<Int> = flowOf(1, 2, 3)
    
        flowA.flatMapLatest { a: Int ->
            doSomeMemoryIntensiveStuffForFlowB(a).map { b: String ->
                doSomeCpuIntensiveStuff(b)
            }
        }.collect { c: Long ->
            println(c)
        }
        
        
        flowA.flatMapLatest { a: Int ->
            doSomeMemoryIntensiveStuffForFlowB(a)
        }.map { b: String ->
            doSomeCpuIntensiveStuff(b)
        }.collect { c: Long ->
            println(c)
        }
    }
    
    fun doSomeMemoryIntensiveStuffForFlowB(a: Int): Flow<String> {
        // Some Memory intensive stuff related to "a" (object creation, etc)
        var memoryGreedyList = listOf<Int>()
    
        repeat(10_000) { memoryGreedyList = memoryGreedyList + listOf(it) }
    
        return flowOf(a.toString(), a.toString(), a.toString())
    }
    
    fun doSomeCpuIntensiveStuff(b: String): Long {
        // Some CPU intensive stuff related to "b" (mapping, etc)
        repeat(10_000) { b.toDouble() * it }
        
        return b.toLong()
    }
    https://pl.kotl.in/sOl65cZgZ
    n
    • 2
    • 1
  • j

    Joffrey

    05/19/2022, 12:44 PM
    Can somebody explain why the coroutine context of a
    suspend fun main
    is considered inactive?
    suspend fun main() {
        println(currentCoroutineContext().isActive) // prints false
    }
    https://pl.kotl.in/sVFnLz9mM
    e
    • 2
    • 17
  • b

    Bastian

    05/19/2022, 3:55 PM
    Kotlin Native - Suspending Functions - Linux x64 For Linux (x64), we are building a library (C++) via Kotlin Native. In the resulting binary, all suspending functions get removed. Does anyone know how to make these functions available in the resulting binary?
    d
    • 2
    • 2
  • a

    Alexandru Hadăr

    05/20/2022, 1:28 PM
    Are there are drawback to the
    currentCoroutineContext()
    function? I want to cancel my listener when my coroutine is no longer active. I don’t have access to the scope, but I can use
    currentCoroutineContext()
    since i’m inside a suspend function. Could this have any negative impact? Like memory leak or a false-positive?
    private suspend fun doEffects(colorChangeCallback: OnColorChangedCallback, valueAnimator: ValueAnimator) {
        val context: CoroutineContext = currentCoroutineContext()
        valueAnimator.addUpdateListener {
            if (context.isActive) {
                // Do your work
            } else {
                // Cancel other work
                valueAnimator.cancel()
            }
        }
    }
    👀 1
    d
    j
    • 3
    • 4
  • k

    kevin.cianfarini

    05/20/2022, 5:36 PM
    Hi folks. Is there any reason `launch`ing a new coroutine in a cancelled scope doesn’t error at all, and instead fails silently?
    s
    j
    +2
    • 5
    • 12
  • j

    janvladimirmostert

    05/21/2022, 10:29 AM
    I'm trying to asynchronously read files and wrap it inside a coroutine That part I got working with AsynchronousFileChannel, but if you try and use AsynchronousFileChannel for files that are inside a jar file (src/main/resources), it throws an UnsupportedException What are the options for asynchronously reading files from inside a JAR other than literally copying all the files out of the JAR on startup and reading it asynchronously from the file system?
    e
    • 2
    • 2
  • g

    Grigorii Yurkov

    05/22/2022, 10:42 AM
    Are coroutines really thread safe? As far as I understand this code can potentially print
    0
    . Because
    i
    can be modified from different threads and kotlin compiler doesn't mark it as
    volatile
    under the hood
    j
    u
    +2
    • 5
    • 35
  • s

    spierce7

    05/22/2022, 6:35 PM
    In a coroutine, I’m using the
    coroutineScope {}
    , calling several async methods, and collecting the deferreds into a
    MutableList<Deferred<Any>>
    . Before the coroutine is over, I call
    deferreds.awaitAll()
    on the list. This isn’t actually necessary if I don’t care about the results, right? A parent suspend function won’t complete until all it’s children coroutines have completed, right?
    :yes: 2
    a
    s
    +2
    • 5
    • 16
  • a

    andylamax

    05/22/2022, 6:53 PM
    Hello there, apart from
    delay
    what other functions actually suspends? I am trying to get an example of a function that actually suspends (like delay), Not just marked as a suspend function and goes into IO operations like many I have seen
    e
    d
    • 3
    • 13
  • j

    John O'Reilly

    05/23/2022, 5:41 PM
    Am looking at some code like following....a function in a view model that creates a
    StateFlow
    using
    stateIn
    (which is called/collected from Fragment)...seemingly it work but feels like there's an issue, or at least some redundancy here but can't put my finger on why right now? (reason that's it's a suspend function instead of property is that it needs to call
    suspendFunctionReturningFlowB
    here but probably other ways around that....)
    suspend fun someFunction() = flowA.combine(suspendFunctionReturningFlowB()) { valA, valB ->
        <some logic>
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
    y
    h
    u
    • 4
    • 16
  • u

    uli

    05/24/2022, 6:24 AM
    Hi all, is there any thing bad with a `collectIn`extension on Flow?
    inline fun <reified T> Flow<T>.collectIn(scope: CoroutineScope, collector: FlowCollector<T>): Job {
        return scope.launch {
            collect(collector)
        }
    }
    It is so obvious, that I was thinking there must be something fundamentally broken if it is not part of the coroutines library.
    ✅ 1
    s
    j
    +2
    • 5
    • 11
  • n

    napperley

    05/24/2022, 10:45 PM
    Is there a feasible way to terminate a process if it takes too long to execute using Coroutines? Currently I have developed a project that simulates a Serverless platform running two Serverless Functions. The platform terminates the function (process) after intercepting its output from a text stream (a maximum output limit is applied). Serverless Function code (truncated):
    // ...
    if (args.isNotEmpty()) args.forEachIndexed { pos, item -> println("Arg ${pos + 1}: $item") }
        println("Ping")
        println("Pong")
        sleep(4u)
        println("Another item...")
        while (true) {
            usleep(500u)
        }
    The function for running the Serverless Function (on the Serverless Platform side):
    private fun CoroutineScope.runFunction(filePath: String, vararg args: String) = launch(context = Dispatchers.Default) {
        println("Running Serverless Function...")
        val file = openProcess(filePath = filePath, args = args)
        file.readLines(5u).forEach { println(it) }
        val processName = extractProcessName(filePath)
        val pid = fetchLatestPid(processName).toInt()
        if (pid > 0) kill(pid, SIGINT)
        file.closeProcess()
    }
    n
    t
    d
    • 4
    • 13
  • t

    Tianyu Zhu

    05/26/2022, 12:39 AM
    Is there an easy way to start a coroutine from a non-suspend function? E.g.
    fun justSomeFunction() {
      launchACoroutine(<http://Dispatchers.IO|Dispatchers.IO>) {
        while (true) {
          // just do some work
        }
      }
    }
    n
    b
    +4
    • 7
    • 24
  • e

    Exerosis

    05/26/2022, 8:29 PM
    coroutineScope {
        println("Entered scope")
        launch {
            println("why not??: ${Thread.currentThread().name}")
        }
    }
    How can I debug this printing nothing at all? I do have a non standard CorountineContext: https://srcb.in/y7TGICTNxq However, it seems to work with everything else so I'm not sure what I'm doing wrong here.
    r
    p
    • 3
    • 5
  • d

    David Stibbe

    05/27/2022, 12:13 AM
    A question: I have several coroutines (~100) sending a message to a channel. This channel reads the messages in a
    for( message in myChannel){ ... }
    loop. I can see, via logging, that the events are being send and I see that they are read in the for loop. However, all except for one of the sending coroutines do not continue and keep being stuck in the suspended state. Any idea what could cause this ? Adding a buffer to the channel seems to help, but I would like to know what causes my issue ?
    z
    j
    s
    • 4
    • 18
  • a

    Arjan van Wieringen

    05/28/2022, 6:34 PM
    If I have a StateFlow, what is the best way to map it to another StateFlow? I get a Flow back from
    map
    , but if I want it to be a StateFlow again I need to use
    stateIn
    but then I require a scope. Or is the mapped result still a stateflow? The usecase is I have a class that I want to expose a StateFlow which is based on another one. But if I want to use
    stateIn
    I need to do this from a coroutine. So it becomes something like this:
    interface History {
        val activeLocation: StateFlow<Location>
        // ...
    }
    
    class Router<R>(
        private val history: History,
        private val routing: Routing<R>,
        private val scope: CoroutineScope
    ) {
        lateinit var activeRoute : StateFlow<R>
            private set
    
        init {
            scope.launch {
                activeRoute = history.activeLocation
                    .map { location -> routing.getRoute(location) }
                    .stateIn(scope)
            }
        }
    }
    I am not sure if this is the most optimal way of doing it... I have the feeling I am overcomplicating stuff.
    r
    s
    +2
    • 5
    • 10
  • a

    andylamax

    05/29/2022, 1:19 AM
    I am currently developing a desktop application, and I have taken an MVVM approach. A ViewModel has no lifecycle in the desktop world, which makes me wonder, if I should inject a coroutine scope, or dispatcher to it. What is the best way to go here?? Should I create a long lasting coroutine scope? or just inject a dispatcher and use withContex while invoke network requests when needed to?? N.B: I want to inject one of these so that I may be able to test it as well
    c
    a
    • 3
    • 6
  • o

    Ovsyannikov Alexey

    05/29/2022, 3:08 AM
    Hello everybody :) I am trying to upgrade coroutines up to 1.6.2, but getting an error like this in any project I tried to update. Is there any info about this?
    e
    • 2
    • 1
  • e

    Exerosis

    05/30/2022, 12:54 AM
    Why is something like this:
    val ctx = currentCoroutineContext()
    val test = object : CoroutineScope {
        override val coroutineContext = ctx
    }
    task = test.launch(context) { block() }
    Not the same as:
    coroutineScope { 
     task = launch(context)
    }
    I know the first one is bad practice so are they both pretty bad?
    e
    z
    • 3
    • 4
  • a

    Arjan van Wieringen

    05/30/2022, 8:22 AM
    I am struggling a bit with some fundamentals I think. I am wondering what is a proper solution for the following: I am creating some sort of Redux architecture, and am now at the part where I am handling effects. These effects should be handled in separate coroutines. Now, the question is as follows. I can implement my
    EffectDispatcher
    two ways:
    // as a suspending function
    interface EffectDispatcher<E> {
        suspend fun dispatchEffect(effect: E)
    }
    
    // return a job
    interface EffectDispatcher<E> {
        val scope: CoroutineScope
        fun dispatchEffect(effect: E): Job
    }
    As far as I understand: In the suspending case (1st) it is the responsibility of the callee to provide a coroutinescope, and in the case of the 2nd the EffectDispatcher itself has a scope in which the effects launch and the callee gets a reference to the dispatched effect job, maybe to cancel it or something, or can cancel every running job with
    scope
    . Is this correct? As my preference goes I'd go for the 2nd option. EDIT: Option number 3 would be to have
    EffectDispatcher<E>
    implement
    CoroutineScope
    . I like that one best actually.
    r
    • 2
    • 13
  • y

    Yoavya

    05/30/2022, 1:52 PM
    is there a problem using
    ConcurrentHashMap
    with coroutines? I see 2 potential issues, assuming that I want to compute a value
    computeIfAbsent
    . I don’t think using
    getOrPut
    is an option since it is not concurrent and the value may already exist: 1. If my compute function is suspending (can be
    runBlocking
    with parallel computation to speed the process) then another coroutine (on the same thread) can run the same
    synchronized
    code. I think that is a problem 2. If I am running on a dispatcher that runs multiple threads and my compute function is suspending again then I can find myself waking up in a different thread then the one that I started in (can that happen?). Will it break the synchronized block inside the
    ConcurrentHashMap
    ? I am not sure what happens here. Is there anything that is coroutine safe equivalent to
    ConcurrentHashMap
    or do you know if there is something planned to solve these issues? (if what I described are issues)
    r
    j
    u
    • 4
    • 50
  • l

    Luke

    05/31/2022, 2:04 PM
    Is there an equivalent of
    collectLatest
    that can be followed by
    launchIn
    instead of wrapping it in a
    launch
    block? For now, I copied the implementation of
    collectLatest
    without the
    collect
    call but I was wondering if something already existed.
    j
    • 2
    • 2
  • s

    Sam

    05/31/2022, 3:25 PM
    I'm to figure out the best way to maintain a semblance of structured concurrency while using an async cache. Currently what I have is a Caffeine AsyncCache, using which I do roughly this:
    suspend fun get(key: K): V = supervisorScope {
        asyncCache.get(key) { k: K, _ -> future { loader.load(k) } }.await()
    }
    The problem I'm having is with cancellation. If multiple consumers are simultaneously waiting for a given key, and one of them stops waiting (i.e. is cancelled), the current implementation will have all the consumers receive a cancellation exception. I can change that by loading the key in a separate scope (e.g. a dedicated CoroutineScope belonging to the cache, rather than a scope belonging to one of the consumers) and replacing
    await()
    with
    asDeferred().await()
    as suggested by the docs. But then I have a problem where nobody can cancel the job. Even if all the consumers stop waiting, the cache entry will continue loading. To handle that problem I would need to introduce some lifecycle management into the cache, e.g. make it
    Closeable
    and have it clean up its own coroutines on close. Has anybody encountered this before and found a nice solution?
    n
    • 2
    • 3
  • e

    Endre Deak

    05/31/2022, 10:01 PM
    can some explain me the following?
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.cancelChildren
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.withContext
    import sun.misc.Signal
    
    suspend fun main() {
        withContext(Dispatchers.Default) {
            Signal.handle(Signal("INT")) {signal ->
                println("RECEIVED ${signal.name}")
                this.coroutineContext.cancelChildren()
            }
    
            launch {
                withContext(Dispatchers.Default) {
                    launch {
                        while (true) {
                            try {
                                println("foo")
                                // delay(2000) // if I put delay here, it fails with an infinite loop on SIGINT
                            } catch (e: Exception) {
                                println("error: ${e.message}")
                            }
                            delay(2000) // if I put delay here outside try-catch, no problem on SIGINT
                        }
                    }
                }
            }
        }
        println("finished")
    }
    e
    • 2
    • 3
  • e

    Exerosis

    06/01/2022, 12:44 AM
    Is it possible to make a CoroutineContext.Element that "falls off" in scopes that branch execution? For example:
    runBlocking(MyContext) {
      launch(<http://Dispatchers.IO|Dispatchers.IO>) {
       //doesn't contain my context anymore
      }
      launch {
       //still contains my context
      }
    }
    I need to be an identifier that indicates that a call is part of the same sequence of calls. Worst case even if I could just get it to log some kind of warning that would be a good start.
    z
    d
    • 3
    • 4
  • e

    Eric Chee

    06/01/2022, 2:54 PM
    UPDATE: I have provided a better Example in the thread. My issue was how to merge a “manual” mutable flow with external sources. Original Question: How can i avoid declaring initialValue twice for StateFlow if i want a private
    MutableStateFlow
    for updating and a public
    StateFlow
    that uses
    .stateIn()
    Example:
    class ExampleViewModel: ViewModel() {
        private val _state = MutableStateFlow<Int>(0) // Set initial value
        
        val state: StateFlow<Int> = _state
            .stateIn(
                scope = ...,
                started = ...,
                initialValue = 0 // Have to duplicate initial value :(
            )
        
        fun onSomeAction() {
            _state.getAndUpdate { lastState -> lastState + 1 }
        }
    }
    j
    e
    • 3
    • 12
Powered by Linen
Title
e

Eric Chee

06/01/2022, 2:54 PM
UPDATE: I have provided a better Example in the thread. My issue was how to merge a “manual” mutable flow with external sources. Original Question: How can i avoid declaring initialValue twice for StateFlow if i want a private
MutableStateFlow
for updating and a public
StateFlow
that uses
.stateIn()
Example:
class ExampleViewModel: ViewModel() {
    private val _state = MutableStateFlow<Int>(0) // Set initial value
    
    val state: StateFlow<Int> = _state
        .stateIn(
            scope = ...,
            started = ...,
            initialValue = 0 // Have to duplicate initial value :(
        )
    
    fun onSomeAction() {
        _state.getAndUpdate { lastState -> lastState + 1 }
    }
}
j

Joffrey

06/01/2022, 2:55 PM
Why do you use
stateIn
here?
It starts a coroutine to collect your existing state flow into a new state flow. You could just return the existing one under a read-only type
e

Eric Chee

06/01/2022, 3:04 PM
because i want the StateFlow to stay alive for the 5s if collector disconnects and needs to reconnect (i.e. activity recreates during config change). In Google’s NowInAndroid app, they use stateIn as well here:
combine(<ExternalFlows>) {
   ...
}.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5_000),
    initialValue = AuthorScreenUiState(AuthorUiState.Loading, ...)
)
j

Joffrey

06/01/2022, 3:11 PM
But you only need to control this lifetime because you start a coroutine. In the example you gave, there are transformations on other flows that are collected into the state flow. In your case, you already have an eternal state flow, no need for a coroutine
e

Eric Chee

06/01/2022, 4:16 PM
So for my learning can you verify if i concluded this right: • A) I should use
.stateIn()
for consuming cold flows into a StateFlow in the case i want those cold flows to stay active depending on the subscriber sharing behavior i defined? • B) If the ViewModel is the creator of a MutableStateFlow, i dont need to use .stateIn() since that mutable stateFlow is already hot and will stay alive for the lifecycle of the ViewModel, regardless if theres no collectors
👍🏻 1
Here’s a more detailed example of what I’m having to detail with. I want to map an external cold flow to State, but ALSO i want the the ability to publish state updates that are separate from those external flows. Any suggestions to improve this:
class ExampleViewModel(externalIntFlow: Flow<Int>): ViewModel() {

    data class State(val msg: String, val isLoading: Boolean = false)

    private val _state = MutableSharedFlow<State>()

    val state = externalIntFlow.map { action ->
            when (action) {
                1 -> State("One")
                else -> State("?")
            }
        }.merge(_state)
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = State("Empty")
        )

    fun someAction() = viewModelScope.launch {
        /*
         Want to update last value of `state` but dont get benefit of state.getAndUpdate
         since state is not mutable from stateIn(), but instead have to use external sharedFlow + state.value.copy()
         */
        _state.emit(state.value.copy(isLoading = false))
        delay(1000)
        _state.emit(State("Success"))
    }
}
j

Joffrey

06/01/2022, 4:48 PM
A) yes, in a way. To be clear, a state flow is really just a flow that has a state inside. If you want to turn cold flows into a state flow, you have to start a coroutine that consumes the cold flows and updates the state of the state flow, hence why
stateIn
needs a scope - to start this coroutine behind the scenes. And it's also why you define a policy to keep this coroutine alive or cancel it. If you create a mutable state flow directly, you just created some sort of observable state variable. It doesn't need any coroutine on its own, because the state will be updated by other pieces of code (they may use coroutines themselves, but they don't need to because state flows have a
state
property that can be updated without suspending). B) true
👍 1
For you current example, you could also define your mutable state flow with initial value, and then instead of using
stateIn
on the external cold flows, you could just manually launch a coroutine that collects them and updates your state flow. This way you can also update your state flow using
_state.value=x
in
someAction()
(it will not be read-only)
💡 1
e

Eric Chee

06/01/2022, 5:12 PM
Wow! super helpful Joffrey 🙂 So given your feedback, is this init{} block doing what you are describing:
class ExampleViewModel(externalIntFlow: Flow<Int>): ViewModel() {

    data class State(val msg: String, val isLoading: Boolean = false)

    private val _state = MutableStateFlow(State("Empty"))
    val state = _state.asStateFlow()
    
    init {
        viewModelScope.launch { 
            externalIntFlow.map { action ->
                when (action) {
                    1 -> State("One")
                    else -> State("?")
                }
            }.also { _state.emitAll(it) }
        }
    }
    
    fun someAction() = viewModelScope.launch {
        _state.getAndUpdate { it.copy(isLoading = false) }
        delay(1000)
        _state.update { State("Success") }
    }
}
j

Joffrey

06/01/2022, 5:14 PM
Yes exactly
🥳 1
e

ephemient

06/01/2022, 5:54 PM
if all you want to do is expose a non-mutable state flow to consumers,
private val _state = MutableStateFlow<Int>(0)
val state: StateFlow<Int>
    get() = _state.asStateFlow()
j

Joffrey

06/01/2022, 5:56 PM
That's exactly what OP did in the last snippet (the main issue was merging that "manual" mutable flow with external sources)
View count: 15