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

chi

11/07/2018, 1:51 AM
I'm trying to implement coroutines on my app, replacing async some async tasks. I have the following block
Copy code
countries.add(country)
launch(Dispatchers.Main) {
                        val exp = runExpensiveCode(country)
                        countries.remove(country)
                        if (exp != null) doAction()
                    }
Copy code
suspend fun runExpensiveCode(country: Country) =
            withContext(Dispatchers.Default) {
                performVeryExpensiveAction(country) // legacy code used in several parts
            }
This still freezes my main screen for some milliseconds, but the Async task implementation doesn't, how can I make this as seamless as possible?
m

mayojava

11/07/2018, 6:09 AM
this looks like something that should work since your suspend function switches context to the
Default
Dispatcher. How did you check that your main thread is still freezing?
c

chi

11/07/2018, 6:33 AM
Runny the app freeszes, that call can be made about ~800 times in succession though
g

gildor

11/07/2018, 7:32 AM
async task implementation is much smoother
What do you actually mean?
Could provide some self contained example
Not clear what exactly going on there and how you use it
Did you tried to profile your app what is actually freezes you app and how your “async task” code looked like
Still your coroutine code should work
There is some error
You just should profile
Also, with coroutine, there’s about 5s delay on the main thread
What does that mean?
did you actually tried to profile it?
running 800 processings in parallel doesn’t sound like a correct solution
maybe you just start too many processings simultaneously
I mean that coroutines implementation itself should not cause any problems
difference can be because AsyncTask is sequentially does this work
even if you run too many of them
It depends what you want
if those processings should be sequential than you just should run many coroutines, instead use something like actor
or use any other way to run only once
also if you can make
runExpensiveCode
cancellable than it will be possible just cancel task during processing and start new one
Just a note, that starting from Android 3.0 AsyncTask are sequential by default and always run on a single thread
See, AsyncTask is actually sequential, just move work to another thread
you can use actor to achive similar behavior: in this case each new task will be run after previous. Also you can use own custom executor to use only 1 thread instead of Default dispatcher
Something like:
Copy code
val bitmapDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
launch(bitmapDispatcher) {
   runExpensiveCode()
}
But solution with actor looks better for me
But as I said, you should first understand what you want from your code: - run each task after previous - replace old task - start tasks with debounce dela - run as many as it possible to finish all of them as soon as possible
when you understand what you want, you can choose correct solution
But than your old solution with AsyncTask is really bad
because you run all of them sequentially
interrupting the main thread
I would profile code to understand what is going on. Maybe you just consumed all the CPU and UI thread doesn’t have resources to render
Because your old code as I said, just run each task after another, so 100 task that run 1 sec would be processed for 100+ seconds
Solution with custom dispatchers that I send above is the easiest way to reproduce this behaviour with coroutines (or actors, which more advanced)
one more thing, don’t forget to close executor when you don’t need it anymore, otherwise this thread will leak
t

Tolriq

11/07/2018, 8:30 AM
Beware of a big difference between AsyncTask and Coroutines too. The threads priority. Aysnctask have priority threads set to background, coroutines obviously don't. So you can easily saturate a low to mid end devices with coroutines. This is something that is not very visible for newcomers or people who don't dive enough in how the things they use works.
👍 1
And I forget but async task is 2 to 4 threads so can run up to 4 tasks depending on your device. If you create a custom dispatcher not limited to 1 you should set priority in the threads.
To fully mimic the performances people usually do use AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher() to get the same limits and thread priority 😉
g

gildor

11/07/2018, 8:46 AM
And I forget but async task is 2 to 4 threads so can run up to 4 tasks depending on your device
@Tolriq It’s not default AsyncTask behaviour anymore. Starting from Android 3 all the tasks are run on a single BG thread sequentially
Also THREAD_POOL_EXECUTOR is not default executor
SERIAL_EXECUTOR is default one
But advice to use
asCoroutineDispatcher()
with this executor is good
also no need to close it and can be shared with other screens
7 Views