Reputation:
I have been reading about Executor in Android documentations. If I understood it correctly, it is used for multiple thread management and it does some of the work for you like spawning new threads when needed. Or you may choose to manage stuff yourself.
In the example below, a group of executors are used instead of one. So it is something like a pool of pool of threads (?).
/**
* Global executor pools for the whole application.
*
* Grouping tasks like this avoids the effects of task starvation (e.g. disk
reads don't wait behind
* webservice requests).
*/
@Singleton
open class AppExecutors(
private val diskIO: Executor,
private val networkIO: Executor,
private val mainThread: Executor
) {
@Inject
constructor() : this(
Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(3),
MainThreadExecutor()
)
fun diskIO(): Executor {
return diskIO
}
fun networkIO(): Executor {
return networkIO
}
fun mainThread(): Executor {
return mainThread
}
private class MainThreadExecutor : Executor {
private val mainThreadHandler = Handler(Looper.getMainLooper())
override fun execute(command: Runnable) {
mainThreadHandler.post(command)
}
}
}
Why would one choose to use a group of executors? What do you achieve with it which you can't with just one executor?
Upvotes: 4
Views: 1478
Reputation: 6891
That's just structuring and assigning the right executor for the right jobs they might execute:
Runnable
s and each thread the executor creates can run one job at a time:
diskIO
is (from the constrcutor
) a Executors.newSingleThreadExecutor()
since the tasks are best queued and executed one at a time to reduce write and read locks or race conditions for example. Hence a SingleThreadExecutor
would run only one task at a time no matter how many are queued to ensure that design. Being a single thread could also mean that it's being used for writing app logs to a file for example which allows for the logs to be written in the proper order as being submitted to the executor. Hence single thread is best at maintaining output as in the order of jobs queued.networkIO
is a Executors.newFixedThreadPool(3)
since the tasks are usually network related like connecting to a server on the internet and performing requests or getting data. These tasks usually make the user wait (could be between seconds to minutes) and need to be executed in parallel and fast to make the wait shorter in case many requests need be performed together. Hence the reason there are 3 threads employed with this executor is to assign the tasks among them and execute together. Order of jobs is not a concern here since jobs take different amount of time to execute but what matters the most is that they're running in parallel.mainThread
is a MainThreadExecutor()
which in an Android app handles the UI and drawing it. The UI should function smoothly and not lag and hence the reason to use the above two executors is to let any heavy task (like writing a file or performing requests) to run in the background or separately from the mainThread
of the app. This executor keeps performing tasks even if the app didn't submit any to it. The tasks it keeps performing is drawing the UI continuously on the screen which constantly repeats. Tasks executed by the mainThread
need to lightweight and fast (time they take are in the order of milliseconds), and so any task that slows it down will be noticed as the UI will lag or glitch with it because the mainThread
is busy finishing that task instead of drawing and updating the UI. The mainThread
here simply uses a Handler
which is part of the Android SDK/architecture, is of a single thread type and behaves like an executor (with some differences) that queues tasks to create/update the UI. Only a Handler
can perform UI tasks, none of the other executors can.Upvotes: 7