Shreyas Patil
08/17/2020, 7:33 AMphldavies
08/17/2020, 9:11 AMubu
08/17/2020, 11:29 AMViewModelScope
from AAC
, which works best for most of simple use cases, but when you want to cancel the job whose lifecycle is very specific, then there is a place for discussion. Suppose an operation should be run starting from onStart()
call until there is a onStop()
call. What would you suggest as good practice here to control this operation’s lifecycle?
I started by creating custom scope, inside which this operation would run, then i realised, that after cancellation this scope can’t be re-used. Then I thought that creating a new scope every time onStart()
is called is a bad idea (or not?). So I switched to a nullable variable holding a reference to a Job
created after launch { ... }
in onStart()
, which I cancel in onStop()
. Seems normal, but what if you want to cancel a lot of different jobs between `onStart()`/()`onStop()` calls? I might want a strange thing to do: having a scope, which, when you cancel it, cancel all its children job, but could be reset to allow new coroutines to run. Perhaps, I do not understand properly which logic stands behind not allowing to re-use cancelled scope.
Thanks!Maciek
08/17/2020, 12:42 PMCarrascado
08/17/2020, 5:02 PMfun suspend foobar() { dostuff }
, if I call that function from a coroutine, I understand from the documentation that calling foobar will suspend the coroutine, so does that mean foobar won't be executed? Why would I want to do that? It does not make any sense in my head. I know I must be understanding something really wrongrkeazor
08/18/2020, 12:16 AMspierce7
08/18/2020, 3:49 AMChannel
I'd use consumeAsFlow
if and only if the Flow was guaranteed to only have a single subscriber, but I'd use receiveAsFlow
if one or multiple subscribers were possible, right?jean
08/18/2020, 10:54 AMviewModelScope
to be able to use internal suspend function, and then use lifeCycleScope
to observe the state flow variable. That leave us with two different coroutines, doesn’t it? Now I’m trying to test my code that use a similar approach and run in the situation where one of the mocked object used from the viewModelScope
always return null regardless of the whenever
mocking configuration.
I stoped using the init
block of my view model to jump start the handling of intent and replace it by a suspend fun, so now my view calls that function which returns the state flow to observe instead of the variable. This solved my null-value-mocked issue.
Is that a better way way? Is there anything I’m not understanding?kevinherron
08/18/2020, 5:23 PMLuis Munoz
08/18/2020, 10:15 PMvar scope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + CoroutineName("myLoop"))
job = scope.launch {
while (this.isActive) {
(0 until appCfg.count).map {
delay(delayBetweenDeviceStarts)
val sn = appCfg.startSn + it
launch{
// DO SOME HTTP OPERATIONS
}
}.joinAll()
}
}
rkeazor
08/19/2020, 3:08 AMpajatopmr
08/19/2020, 9:44 AMGus
08/19/2020, 9:52 AMFlow<Thingy>
and, in some cases, that flow/job should be cancelled if no new Thingy
has been emitted in the past X seconds (and I'll need to close an underlying connection when the flow is cancelled). Is there any readily-available utility to achieve this? If not, does anyone have any pointers on how to do this in an idiomatic way?
If I have to implement it myself, I'm thinking of implementing it as two separate functions: getThingy(): Flow<Thingy>
and getThingy(timeoutMillis: Int): Flow<Thingy>
. getThingy()
would return a cancellable flow, and getThingy(timeoutMillis)
would use the former function behind the scenes and add the timeout logic. I'm thinking of using Timer.schedule()
in the timeout logic so I can cancel the timer and start a new one each time a new Thingy
is emitted, but I wonder whether there's a better way to do this.Lukas Lechner
08/19/2020, 12:33 PMCoroutineExceptionHandler
in a viewModelScope
when e.g. initialising an Android ViewModel
? I want that exceptions by every Coroutine that are then started in this scope are then handled by the handler.tylerwilson
08/19/2020, 5:25 PMUncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode@7eec8
Anybody have any clues where to look? This same code worked in previous version…Gus
08/19/2020, 9:51 PMretrieveMessages().map {
File("the path").writeBytes(it.message)
it.ack()
}
It's crucial that the ACK message be only sent to the server if and when the the incoming message is safely stored. That's why the ACK is explicit.
I started to model this with a Flow<*>
but I'm finding it rather cumbersome because I'd have to track all the unacknowledged messages so I won't close the connection too soon, which I think is a code smell since flows are supposed to be cold streams and therefore the underlying resource(s) should only be active while the flow is active. So for that reason I'm starting to question whether I should be using flows (or something like it) here, compared to just getting a callback to process each incoming message and sending the ACK to the server when the callback completes without errors; e.g.:
retrieveMessages { message -> File("the path").writeBytes(message) }
Thoughts?hultgren
08/20/2020, 1:21 AM1.3.9
(the problem goes away if I use 1.3.9-native-mt
).
The project has the following dependencies:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "io.ktor:ktor-client-core:1.4.0"
It's the task compileIosMainKotlinMetadata
that fails with the following error:
e: Could not find "kotlinx-coroutines-core_concurrentMain"
Anyone else seen this?phldavies
08/20/2020, 9:02 AMJob(parent = coroutineContext[Job])
?Czar
08/20/2020, 10:03 AMconsumeEach
assumes that the actor is temporary and will be closed after each message is processed, while in my case, the actor should probably persist, even if at the moment there are no new events.
Another problem with actor is, that by making it unbounded I can run into OOM if there is a spike in events count, this is where I need backpressure and cannot just read events from the repository with constant rate.Gopal S Akshintala
08/20/2020, 2:15 PMsuspend fun findBigPrime(): BigInteger =
withContext(Dispatchers.Default) {
BigInteger.probablePrime(4096, Random())
}
Nikky
08/20/2020, 3:04 PMwhile(true) { launch { doJob() }: delay(..) }
be the preferred solution ?Pablo
08/20/2020, 3:24 PMFlow
so what I want to do is change the List<Animals>
to a Flow<List<Animals>>
but then, I'm losing the mappers on my use-case
and on my presentation
? Is there any way to keep mapping the values from the data
layer? I mean, my dao now returns a Flow<List<Animals>>
do I have to change also the repository return to be a Flow<List<Animals>>
? I had a return List<AnimalsMappedToDomain>
, but now how can I do that mapper? What would be the flow from repository to presentation using the mappers?jean
08/20/2020, 4:33 PMlifecycleScope.launchWhenCreated {
viewModel.state
.onEach { updateState(it) }
.onStart { inputsCollector.loadData() }
.launchIn(this)
inputsCollector
emit values on a channel when loadData
is called, the values are then used by a state machine.
the problem here is that inputsCollector.loadData()
is called too soon, before the function in the state machine collects the values (I guess) even though I call that function before launching the coroutine. If I had delay(100)
before inputsCollector.loadData()
everything works fine.
Any idea how when I should called inputsCollector.loadData()
?Vincent Williams
08/20/2020, 4:52 PMprivate fun subscribeToViewModel() = lifecycleScope.launch(Dispatchers.Main) {
viewModel.viewState.collect { onViewStateEvent(it) }
viewModel.navigationEvents.collect { onNavigationEvent(it) }
}
This code doesnt seem to work. It only collects the first flow. Do I need to have two separate launch
statements? Is there an easier way to do this as it gets a little messy with 3 or 4 flowssean
08/21/2020, 1:03 AMsuspend fun doSomeWork(): Thing = supervisorScope {
val foo = async {
val result = async {
fetchResultFromNetwork()
}
val otherResult = async {
fetchOtherResultFromNetwork()
}
result.await() + otherResult.await()
}
val bar = async {
anotherThing()
}
Thing(foo.await(), bar.await())
}
Would you go with:
a) suspend fun + scope builder
suspend fun doSomeWork(): Thing = supervisorScope {
val foo = async {
fetchAndMergeResults()
}
val bar = async {
anotherThing()
}
Thing(foo.await(), bar.await())
}
suspend fun fetchAndMergeResults = coroutineScope {
val result = async {
fetchResultFromNetwork()
}
val otherResult = async {
fetchOtherResultFromNetwork()
}
result.await() + otherResult.await()
}
b) Extension on CoroutineScope which returns a Deferred
suspend fun doSomeWork(): Thing = supervisorScope {
val foo = foo()
val bar = async {
anotherThing()
}
Thing(foo.await(), bar.await())
}
fun CoroutineScope.foo(): Deferred<Foo> {
async {
val result = async {
fetchResultFromNetwork()
}
val otherResult = async {
fetchOtherResultFromNetwork()
}
result.await() + otherResult.await()
}
}
c) Something else?
Any thoughts on what's preferred between the first two options? I'm aware of the conventions on suspend fun vs extension on CoroutineScope described in this video: https://youtu.be/hQrFfwT1IMo?t=2431▾
Vincent Williams
08/21/2020, 4:19 PMprivate fun fetchData() = viewModelScope.launch(dispatcher) {
repository.fetchData()
.onEach {
//success
}
.catch { Timber.e(it) }
.launchIn(this)
}
Why is this still crashing my app when an exception occurs? should catch
be catching that and not crashing?fkrauthan
08/22/2020, 4:44 AMDavide Giuseppe Farella
08/22/2020, 2:12 PMTest finished with active jobs
scope.launch(Io) {
withTimeout(500) {
while (stack.isEmpty()) {
delay(1) // await
}
result.data = stack.removeFirst()
}
}
While this completes successfully
val j = scope.launch(Io) {
while (stack.isEmpty()) {
delay(1) // await
}
result.data = stack.removeFirst()
}
scope.launch {
delay(500)
j.cancel()
}
Gopal S Akshintala
08/23/2020, 5:49 AMThread.currentThread().name
, what exactly does @coroutine#id
indicate (I understand it's an identifier to coroutine, but what exact piece of work does it indicate)? Is it the work the current thread does from one suspension point to till it encounters another suspension point? What exactly is a @coroutine#id
physically translates to?Gopal S Akshintala
08/23/2020, 6:14 AMdelay()
Gopal S Akshintala
08/23/2020, 6:14 AMdelay()
octylFractal
08/23/2020, 6:16 AMdelay
is actually one of the more complex bits, the specifics depend on the dispatcher you're usingTimer
to schedule and execute delays
if you're on the Default dispatcher, it has its own scheduling logic that is very complex and can be viewed in kotlinx.coroutines.EventLoop
and other classesGopal S Akshintala
08/23/2020, 6:23 AMoctylFractal
08/23/2020, 6:25 AMGopal S Akshintala
08/23/2020, 6:28 AMoctylFractal
08/23/2020, 6:30 AMGopal S Akshintala
08/23/2020, 6:33 AM<http://Dispatchers.IO|Dispatchers.IO>
how does "Thread doesn't block work? When I launch coroutines on <http://Dispatchers.IO|Dispatchers.IO>
it spawns a large pool of threads, which eventually block and wait for the data, right?octylFractal
08/23/2020, 6:34 AM<http://Dispatchers.IO|Dispatchers.IO>
does block, it's the exception to allow compatibility with blocking codeGopal S Akshintala
08/23/2020, 6:36 AM<http://Dispatchers.IO|Dispatchers.IO>
would allocate me one thread, which blocks on InputStream
, right?octylFractal
08/23/2020, 6:36 AMGopal S Akshintala
08/23/2020, 6:37 AMoctylFractal
08/23/2020, 6:38 AMGopal S Akshintala
08/23/2020, 6:42 AMoctylFractal
08/23/2020, 6:44 AMsuspend
rather than blockGopal S Akshintala
08/23/2020, 6:47 AMoctylFractal
08/23/2020, 6:48 AMDispatchers.Default
and <http://Dispatchers.IO|Dispatchers.IO>
may share threads, if needed they will use new ones to their pool limitGopal S Akshintala
08/23/2020, 6:50 AMoctylFractal
08/23/2020, 6:51 AMDefault
, that's part of the "lightweight" bit -- you don't really need a bunch of thread pools, you can mostly use the 2 given to youGopal S Akshintala
08/23/2020, 7:33 AM