https://kotlinlang.org logo
#coroutines
Title
# coroutines
d

dave08

06/24/2018, 3:34 PM
I'm using an event queue based on a
Channel
like this:
Copy code
val eventQueue = Channel<ApplicationInstallRequest>()

	init {
		initEventProcessor()
	}

	private fun initEventProcessor() = launch(Unconfined) {
		for (request in eventQueue) {
			processRequest(request)
		}
	}

	override fun onEvent(event: ApplicationInstallRequest) {
		eventQueue.sendBlocking(event)
	}
from an Android SyncAdapter, but even when the sync is finished, the process keeps on going (some kind of leak...), does Unconfined have it's own thread or something?
l

louiscad

06/24/2018, 3:55 PM
Android kills processes when it's out of RAM or when a non foreground, long running process consumes too much resources like CPU. So if the RAM pressure is low, it keeps the processes alive, so the apk don't need to be reloaded in RAM from storage again next time something is going on with this same app (like another sync session)
d

dave08

06/24/2018, 3:57 PM
Right, but then all processes should be stopped...? The downloading is continuing even though the sync adapter stops and the attached service is killed.
Also, I don't think it ran out of ram, I'm not getting those in the logs at least, and I have enough Ram and no other apps running...
For some reason, I think it thinks the sync is finished because of coroutines, so it just keeps going and finishes...
But the coroutine is still running...
l

louiscad

06/24/2018, 4:05 PM
Right, but then all processes should be stopped...?
No, you misread/misunderstood my statement (which is inspired from Android docs)
I have enough Ram and no other apps running
That's why your process doesn't get killed by Android
d

dave08

06/24/2018, 4:07 PM
It would be fine if the Service was still alive... the problem is that the service is dead, and the process keeps running...
l

louiscad

06/24/2018, 4:07 PM
Anyway, do what is right in your app, understand the lifecycle of the components you use, and make your app do only necessary work. having a parent job that you cancel in methods like
onDestroy()
is a good idea also
t would be fine if the Service was still alive... the problem is that the service is dead, and the process keeps running...
That's totally normal.
d

dave08

06/24/2018, 4:08 PM
So I don't have to kill it on
onDestroy
?
Isn't that considered a leak?
l

louiscad

06/24/2018, 4:09 PM
The process just sits there in case the service or any other component in your app is started again, so it's not needed to load the apk from scratch back again
What do you call "kill" ?
In this:
So I don't have to kill it on
onDestroy
?
d

dave08

06/24/2018, 4:09 PM
Meaning cancel the coroutine (and the download)
l

louiscad

06/24/2018, 4:10 PM
kill and cancel are not the same things
When your process is killed, you can rest assured everything in your app has been aborted
But when you cancel a coroutine, it can take some time to be effectively cancelled, or it can not support cancellation, and just keep on running until it reaches a point that detects cancellation or until it completes
Yes you should cancel your service related coroutines in
onDestroy()
, especially if they involve I/O or other limited resources that may drain the battery or consume power/resources needlessly when you should have stopped the work
d

dave08

06/24/2018, 4:14 PM
The problem is that they only run after
onDestroy
, which was my problem in the first place 🤕 , so the download won't run... I think I'm doing something wrong with coroutines...
At first, I had CompletableDeferred's all over the place and it was working.. when I tried simplifying the code, and using
suspend fun
instead of all the actors and launches... I started having these problems...
l

louiscad

06/24/2018, 4:18 PM
I'm not sure what you're trying to do, but I think you're going overcomplicated, probably because there's something you didn't fully grasp. If you can tell me what you're using coroutines for in this problematic case, I can probably help you
d

dave08

06/24/2018, 4:23 PM
I'm trying to download files in parts, as part of a bigger sync process, and a sync might be started from another service at the same time. I'm using the channel as a barrier that only one download should run at a time. (Afterwards I was planning to check for each request if that same file is already underway and check if the user cancelled download on that request...
l

louiscad

06/24/2018, 4:30 PM
@dave08 Can the user manually cancel the download, and is it tied to the same mechanism you are trying to use for cancelling when the sync adapter or the service is destroyed?
d

dave08

06/24/2018, 4:53 PM
No, he can only cancel by running the sync again (not my code, I'm stuck with that api for now..)
l

louiscad

06/24/2018, 6:37 PM
So what you need is either a
Mutex
(from kotlinx.coroutines, which suspends instead of blocking), which you put in a top level val or in an
object
, or an
actor
. And not a big deal if sync continues after the service or sync adapter is stopped. If there's not internet, it will fail and stop by itself, and if the system really needs the resources, it will throttle or kill the process
d

dave08

06/24/2018, 7:20 PM
But any reference to the application context won't fail? I tried actors, it gets very messy with tons of extra classes with all the different requests and responses. And completable deferreds... the new actors look much better simple smile , I hope they'll come out soon...
l

louiscad

06/25/2018, 11:32 AM
@dave08
sendBlocking
doesn't suspend but blocks while the underlying
send
is suspended (if
offer
didn't work in the first place). You shall never use
sendBlocking
from a coroutine
d

dave08

06/25/2018, 11:34 AM
I stand corrected 🙂, in my case, that's what I needed since I'm running in a background thread in a
Service
...
d

dekans

06/26/2018, 11:00 AM
In the same use cas, I use
offer
and it works perfectly
l

louiscad

06/26/2018, 12:41 PM
offer
has different behaviors following the type or capacity of the channel though, it's important to be sure it's right for the use case
3 Views