how to implement executors for splash screen in android - android

I read on post that Coroutine isn't a good practice for using on splash screen and Executors is the best for that because Coroutine takes a time to start more than Executors, but I didn't find an example of implementing that, is the Executors is the normal java Executor class which used to manage thread pool?

First, for splash best performance you must read the link mentioned in comments by William Reed here and here and also this other link here
and when you read the articles focus on these words
The measurements were made on a debuggable app differ surprisingly
from production performance
As pointed out by Jake Wharton, the difference is partly due to
ExecutorService being preloaded by the Zygote, a special part of the
Android framework that shares code between processes. Other
concurrency frameworks which, like coroutines, aren’t preloaded, will
also have a comparatively high initialization cost. That said,
coroutines have a lot of advantages over ExecutorService. They’ve got
scopes, suspending functions, they’re much more lightweight than
threads, etc. The general recommendation to use them in Android
applications is sound, but their impressive feature set has an
initialization cost at the moment. Perhaps the Kotlin and Android
teams will be able to optimize this in the future. Until then, best to
avoid using coroutines in your Application class, or in your main
Activity, if startup time is a primary concern.
Second, for your other part of the question I think that is the Executor you asked about
And here how to use it
1 - To execute code in the main thread
// Create an executor that executes tasks in the main thread.
val mainExecutor = ContextCompat.getMainExecutor(this)
// Execute a task in the main thread
mainExecutor.execute {
// You code logic goes here.
}
2 - To execute code in a background thread
// Create an executor that executes tasks in a background thread.
val backgroundExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
// Execute a task in the background thread.
backgroundExecutor.execute {
// Your code logic goes here.
}
// Execute a task in the background thread after 3 seconds.
backgroundExecutor.schedule({
// Your code logic goes here
}, 3, TimeUnit.SECONDS)
Remember to shut down the executor after using.
backgroundExecutor.shutdown(); // or backgroundExecutor.shutdownNow();
3 - To execute code in a background thread and update UI on the main thread.
// Create an executor that executes tasks in the main thread.
val mainExecutor: Executor = ContextCompat.getMainExecutor(this)
// Create an executor that executes tasks in a background thread.
val backgroundExecutor = Executors.newSingleThreadScheduledExecutor()
// Execute a task in the background thread.
backgroundExecutor.execute {
// Your code logic goes here.
// Update UI on the main thread
mainExecutor.execute {
// You code logic goes here.
}
}
what is the best for performance on Debug (Production May vary as mentioned before)?- Executor, and Coroutine takes much time based on trying both of them with this function
fun TestPerformance(){
val startTime = System.nanoTime()
// DEBUG in handler 3002952707
Handler(Looper.getMainLooper()).postDelayed({
val endTime = System.nanoTime()
println("DEBUG in handler ${endTime-startTime}")
}, 3000)
// DEBUG in backgroundExecutor 3001161822
val backgroundExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
backgroundExecutor.schedule({
val endTime = System.nanoTime()
println("DEBUG in backgroundExecutor ${endTime-startTime}")
}, 3, TimeUnit.SECONDS)
// DEBUG in GlobalScope 3046312603
GlobalScope.launch {
delay(3000)
val endTime = System.nanoTime()
println("DEBUG in GlobalScope ${endTime-startTime}")
}
}
As you can see I add another way to the comparison with handler too, and look at the comments in the code, both handler and Executor faster than Coroutine and sometimes handler wins and sometimes Executor wins, both of them gives the same performance.
and if you want more examples for how to use handler check this answer it's a great help and I used it in my answer.

once you have an Executor instance, you can submit multiple tasks to it, and have them executed one after another. You can't do that simply with a raw Thread.
Creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time.
Executors.newSingleThreadExecutor().execute {
// todo.
}

Related

Which way should I choose between WorkManager and Handler when I run a task periodically?

In the application’s life-cycle, I hope to get the maxAmplitude of MediaRecorder every 100ms just like Code A
I have read the article both this one and that one
I was told that
WorkManager is the primary recommended API for background processing. Scheduled tasks that start at a later time and can run periodically
and
When application is running and we want to schedule or run a task at a specific time then it is recommended to use Handler class in conjunction with Timer and Thread. Instead of using Alarm Manger, Job Scheduler etc. it is easier and much more efficient to use Handler.
1: Which way should I choose between WorkManager and Handler when I run a task periodically ?
2: And more, is Timer().scheduleAtFixedRate(timerTask{ }) good way?
Code A
private fun getAmplitude(): Int {
var result = 0
mRecorder?.let {
result = it.maxAmplitude
}
return result
}

When to use Kotlin Coroutines to optimize code performance

Currently, I am experimenting a bit with kotlin coroutines and I asked myself a question: Is there any significant performance gain when using coroutines? Let's look at some (theoretical) examples:
Example one
val myUnsortedList = listOf(1, 2, 5, 6, 3, 10, 100)
fun doStuffWithSorted(list: List<Int>) { ... }
// Without coroutine
val sortedList = myUnsortedList.sorted()
doStuffWithSorted(sortedList)
coroutineScope {
launch {
val sortedList = myUnsortedList.sorted()
doStuffWithSorted(sortedList)
}
}
Example two
fun doSomeHeavyOperations() { // doing heavy Operations but non blocking }
fun main() { doSomeHeavyOperations() }
//////////////// VERSUS //////////////////////
suspend fun doSomeHeavyOperations() { // doing heavy Operations but non blocking }
suspend fun main() {
coroutineScope {
launch {
doSomeHeavyOperations()
}
}
}
There are many more examples and maybe one of you could give some, where using coroutines is recommended. So my final question is (including the question above): When should it be considered to optimize the code performance with coroutines, and when is the expense bigger than the gained performance?
Coroutines are primarily a tool for computations that involve a lot of waiting, think about network calls executed in sync. in those cases the calling thread does nothing but wait for the server's response.
to remove this waiting issue we use async programming, AKA callbacks. so you start a network call and specify a callback which will be invoked once the result is ready. but the callback model has its own issues, the callback hell, as in below code.
fun someAPICalls(){
getToken(){ token ->
login(token) { userID ->
getUser(userID){ user ->
// Save user in DB
// This nesting can be even deeper
}
}
}
}
As you can see this code is not something that can be considered manageable. with kotlin's suspending functions, all this reduces to
fun someAPICalls() = viewModelScope.launch{
val token = getToken() // suspending function calls (Retrofit, Room)
val userId = login(token)
val user = getUser(userId)
}
As you can see, this is very close to how sequential code is written.
Even though there are other options(RX etc) available to solve the callbacks issue, they do introduce their own semantics which you need to learn. on the other hand writing coroutines code is not that different from its sequntial counterpart, you only have to learn a few basic constructs(Dispatchers, Builders etc), and this is something that makes coroutines the best choice in this scenario.
Apart from this there are some other scenarios where coroutines can be used effectively, one such use case is the practice of thread offloading used in UI frameworks such as Android. when you want to execute a long running CPU bound operation, you don't do it on UI thread, instead you offload the operation to a background thread. this can also be acomplished very cleanly using one of the couroutines builder such as lifecycleScope.launch(Dispatchers.Default) {}
Where coroutines should be avoided?
Coroutine is an abstraction above thread, its something that requires a thread to execute in same way as a thread requires a CPU core to execute. because of this there is certain overhead introduced by the coroutine managment, so if you need to perform a long running CPU bound operation which may require use of multiple threads, you are better off using threads (Java ExecutorService etc.).

Why use withContext() vs Async-await, isn't the goal of coroutines to achieve concurrency?

I am learning about threading and kotlin coroutines to load my UI and repository concurrently, but then "withContext()" as an alternative to async-await.
If I understood "withContext()" correctly and it executes one task after another waiting for the previous task to finish, why ever use it? Is there another concept I'm missing?
withContext is a suspending function that allows to execute a specific piece of code in a different coroutine context. It is in particular useful when you want to execute something in a different dispatcher.
For instance, you could have some code run in the default dispatcher with multiple threads, but then use a value produced by that code to update some UI in the UI thread. In that case, you're not looking for concurrency, the computation on the default dispatcher has to happen before updating the UI because you need the result:
val result = someComputation()
withContext(Dispatchers.Main) {
updateUI(result)
}
Of course, even if the computation and the update of the UI are not concurrent, their sequence can be concurrent with other pieces of code:
scope.launch(Dispatchers.Default) {
val result = someComputation()
withContext(Dispatchers.Main) {
updateUI(result)
}
}
If you need to execute concurrent things, you can use coroutine builders like launch and async. However, using async { ... } immediately followed by .await() defeats the purpose of concurrency, because the code between the async and await() calls is precisely what will run concurrently with the async's body:
val deferred = async { computeSomeValue() }
somethingConcurrentWithAsyncBody()
val result = deferred.await()
You can read more about how to organize your calls to achieve concurrency in the part of the doc about composing suspend functions.

When and why would one use a group of Executor

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?
That's just structuring and assigning the right executor for the right jobs they might execute:
It's nicely put in a single class for easy reuse.
Three types of executors are employed, each for a specific type of task it could run. Remember that executors have threads to execute jobs or Runnables 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.

How AsyncTask works in Android

I want to know how AsyncTask works internally.
I know it uses the Java Executor to perform the operations but still some of the questions I am not understanding. Like:
How many AsyncTask can be started at a time in an Android app?
When I start 10 AsyncTask, will all tasks will run simultaneously or one by one?
I have tried with 75000 AsyncTask to test the same. I don't get any problem and seems like all the tasks will be pushed to stack and will run one by one.
Also when I start 100000 AsyncTasks, I start getting OutOfMemoryError.
So is there any limit of no of AsyncTask which can be run at a time?
Note: I have tested these on SDK 4.0
AsyncTask has a rather long story.
When it first appeared in Cupcake (1.5) it handled background operations with a single additional thread (one by one). In Donut (1.6) it was changed, so that a pool of thread had begun to be used. And operations could be processed simultaneously until the pool had been exhausted. In such case operations were enqueued.
Since Honeycomb default behavior is switched back to use of a single worker thread (one by one processing). But the new method (executeOnExecutor) is introduced to give you a possibility to run simultaneous tasks if you wish (there two different standard executors: SERIAL_EXECUTOR and THREAD_POOL_EXECUTOR).
The way how tasks are enqueued also depends on what executor you use. In case of a parallel one you are restricted with a limit of 10 (new LinkedBlockingQueue<Runnable>(10)). In case of a serial one you are not limited (new ArrayDeque<Runnable>()).
So the way your tasks are processed depends on how you run them and what SDK version you run them on.
As for thread limits, we are not guaranteed with any, yet looking at the ICS source code we can say that number of threads in the pool can vary in range 5..128.
When you start 100000 with default execute method serial executor is used.
Since tasks that cannot be processed immediately are enqueued you get OutOfMemoryError (thousands of tasks are added to the array backed queue).
Exact number of tasks you can start at once depends on the memory class of the device you are running on and, again, on executor you use.
let us dive deep into the Android’s Asynctask.java file to understand it from a designer’s perspective and how it has nicely implemented Half Sync-Half Async design pattern in it.
In the beginning of the class few lines of codes are as follows:
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
/**
* An {#link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
The first is a ThreadFactory which is responsible for creating worker threads. The member variable of this class is the number of threads created so far. The moment it creates a worker thread, this number gets increased by 1.
The next is the BlockingQueue. As you know from the Java blockingqueue documentation, it actually provides a thread safe synchronized queue implementing FIFO logic.
The next is a thread pool executor which is responsible for creating a pool of worker threads which can be taken as and when needed to execute different tasks.
If we look at the first few lines we will know that Android has limited the maximum number of threads to be 128 (as evident from private static final int MAXIMUM_POOL_SIZE = 128).
Now the next important class is SerialExecutor which has been defined as follows:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
The next important two functions in the Asynctask is
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
and
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
AS it becomes clear from the above code we can call the executeOnExecutor from exec function of Asynctask and in that case it takes a default executor. If we dig into the sourcecode of Asynctask, we will find that this default executor is nothing but a serial executor, the code of which has been given above.
Now lets delve into the SerialExecutor class. In this class we have final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();.
This actually works as a serializer of the different requests at different threads. This is an example of Half Sync Half Async pattern.
Now lets examine how the serial executor does this. Please have a look at the portion of the code of the SerialExecutor which is written as
if (mActive == null) {
scheduleNext();
}
So when the execute is first called on the Asynctask, this code is executed on the main thread (as mActive will be initialized to NULL) and hence it will take us to the scheduleNext() function.
The ScheduleNext() function has been written as follows:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
So in the schedulenext() function we initialize the mActive with the Runnable object which we have already inserted at the end of the dequeue. This Runnable object (which is nothing but the mActive) then is executed on a thread taken from the threadpool. In that thread, then "finally "block gets executed.
Now there are two scenarios.
another Asynctask instance has been created and we call the execute method on it when the first task is being executed.
execute method is called for the second time on a same instance of the Asynctask when the first task is getting executed.
Scenario I : if we look at the execute function of the Serial Executor, we will find that we actually create a new runnable thread (Say thread t) for processing the background task.
Look at the following code snippet-
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
As it becomes clear from the line mTasks.offer(new Runnable), every call to the execute function creates a new worker thread. Now probably you are able to find out the similarity between the Half Sync - Half Async pattern and the functioning of SerialExecutor. Let me, however, clarify the doubts. Just like the Half Sync - Half Async pattern's Asynchronous layer, the
mTasks.offer(new Runnable() {
....
}
part of the code creates a new thread the moment execute function is called and push it to the queue (the mTasks). It is done absolutely asynchronously, as the moment it inserts the task in the queue, the function returns. And then background thread executes the task in a synchronous manner. So its similar to the Half Sync - Half Async pattern. Right?
Then inside that thread t, we run the run function of the mActive. But as it is in the try block, the finally will be executed only after the background task is finished in that thread. (Remember both try and finally are happening inside the context of t). Inside finally block, when we call the scheduleNext function, the mActive becomes NULL because we have already emptied the queue. However, if another instance of the same Asynctask is created and we call execute on them, the execute function of these Asynctask won’t be executed because of the synchronization keyword before execute and also because the SERIAL_EXECUTOR is a static instance (hence all the objects of the same class will share the same instance… its an example of class level locking) i mean no instance of the same Async class can preempt the background task that is running in thread t. and even if the thread is interrupted by some events, the finally block which again calls the scheduleNext() function will take care of it.what it all means that there will be only one active thread running the task. this thread may not be the same for different tasks, but only one thread at a time will execute the task. hence the later tasks will be executed one after another only when the first task complets. thats why it is called SerialExecutor.
Scenario II: In this case we will get an exception error. To understand why the execute function cannot be called more than once on the same Asynctask object, please have a look at the below code snippet taken from executorOnExecute function of Asynctask.java especially in the below mentioned portion:
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
AS from the above code snippet it becomes clear that if we call execute function twice when a task is in the running status it throws an IllegalStateException saying “Cannot execute task: the task is already running.”.
if we want multiple tasks to be executed parallely, we need to call the execOnExecutor passing Asynctask.THREAD_POOL_EXECUTOR (or maybe an user defined THREAD_POOL as the exec parameter.
You can read my discussion on Asynctask internals here.
AsyncTasks has a fixed size queue internally for storing delayed tasks. The queue size by default is 10. For example if you start 15 your tasks in a row, then first 5 will enter their doInBackground(), but the rest will wait in the queue for free worker thread. As one of the first 5 finishes, and thus releases the worker thread, a task from the queue will start execution. In this case at most 5 tasks will run together.
Yes, there is limit of how many tasks can be run run at a time. So AsyncTask uses thread pool executor with limited max number of the worker threads and the delayed tasks queue use fixed size 10. Max number of worker threads is 128. If you try to execute more than 138 custom tasks your application will throw the RejectedExecutionException.
How many AsyncTask can be started at a time in an Android app?
AsyncTask is backed by a LinkedBlockingQueue with a capacity of 10 (in ICS and gingerbread). So it really depends on how many tasks you are trying to start & how long they take to finish - but it's definitely possible to exhaust the queue's capacity.
When I start 10 AsyncTask, will all tasks will run simultaneously or one by one?
Again, this depends on the platform. The maximum pool size is 128 in both gingerbread and ICS - but the *default behavior* changed between 2.3 and 4.0 - from parallel by default to serial. If you want to execute in parallel on ICS you need to call [executeOnExecutor][1] in conjunction with THREAD_POOL_EXECUTOR
Try switching to the parallel executor and spam it with 75 000 tasks - the serial impl. has an internal ArrayDeque that has no upper capacity bound (except OutOfMemoryExceptions ofc).

Categories

Resources