banner



How To Create New Thread In Android

Threading in Android

A detailed walkthrough on Android threading

Satya Pavan Kantamani

Introduction

Every Android developer, at one point or another, needs to deal with threads in their application. By default, a thread does three things: starts, does some work, and terminates.

It's suited for small pieces of work but not suited for long tasks where the thread continues tasks to be performed.

As a thread dies after doing some work, we need to have some sort of loop running on the thread to keep it alive. But we should be able to terminate them when required.

In addition, we may need a sort of queue that the loop can pull work from to work on. Also, we may require some other thread that creates work packets and pushes them into the queue for execution.

Doing all this stuff and maintaining their states creates a lot of ambiguity. However, Android provides a set of classes to do this stuff.

Android Thread Classes

When an application is launched, Android creates its own Linux process. Beside this system, it creates a thread of execution for that application called the main thread or UI thread. The main thread is nothing but a handler thread. The main thread is responsible for handling events from all over the app, like callbacks associated with the life-cycle information or callbacks from input events. It can also handle events from other apps.

Any block of code that needs to be run is pushed into a work queue and then serviced by the main thread. As the main thread does so much work, it's better to offer longer works to other threads so as not to disturb UI thread from its rendering duties. It's essential to avoid using the main thread to perform any operation that may end up keeping it blocked resulting in ANRs (application not responding).

Network operations or database calls or the loading of certain components are some examples that may cause the blocking of the main thread when they are being executed on the main thread. They're executed synchronously, which means the UI will remain completely unresponsive until the task gets completed. To avoid this situation, they're usually executed in separate threads, which avoids blocking the UI while the tasks are being performed. This means they're executed asynchronously from the UI.

Android provides many ways of creating and managing threads, and there are many third-party libraries that make thread management a lot easier. Each threaded class is intended for a specific purpose; however, picking the right one that suits our needs is very important.

The different thread classes available are:

AsyncTask: Helps get work on/off the UI thread

HandlerThread: Thread for callbacks

ThreadPoolExecutor: Running lots of parallel work

IntentService: Helps get intents off the UI thread

AsyncTask

AsyncTask enables the proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without the use of threads or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and doesn't constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods, it's highly recommended you use the various APIs provided by the java.util.concurrent package, such as Executor, ThreadPoolExecutor, and FutureTask.

When an asynchronous task is executed, the task goes through four steps:

1. onPreExecute(): Invoked on the UI thread before the task is executed. This step is normally used to do something before the task gets started — for instance, simply showing a progress dialog in the user interface.

2. doInBackground(Params…): Invoked on the background thread after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step, and it sends the result to the onPostExecute(). This step can also use publishProgress(..) to publish one or more units of progress.

3. onProgressUpdate(Progress…): Invoked on the UI thread after a call to publishProgress(..). This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.

4. onPostExecute(Result): Invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

A task can be canceled at any time by invoking cancel(boolean…). We need to do this by checking whether the task is canceled or running.

Implementation

            private class AsyncTaskRunner extends AsyncTask<String, String, String> {            @Override  protected void onPreExecute() {
progressDialog.show();
}
@Override protected String doInBackground(String... params) { . doSomething();
publishProgress("Sleeping..."); // Calls onProgressUpdate()
return resp;
}
@Override protected void onPostExecute(String result) {
// execution of result of Long time consuming operation . progressDialog.dismiss();
updateUIWithResult() ;
}
@Override protected void onProgressUpdate(String... text) {
updateProgressUI();
}
}

When to use AsyncTask

AsyncTask is the perfect solution for the short work that ends quickly which requires frequent UI updates.

However, the async task falls short if you need your deferred task to run beyond the lifetime of the activity/fragment. It's worth noting even something as simple as screen rotation can cause the activity to be destroyed.

Order of execution

By default, all created AsyncTasks will share the same thread and be executed in a sequential fashion from a single message queue. Synchronous execution affects individual tasks.

If we want tasks to execute parallelly, we can use THREAD_POOL_EXECUTOR.

HandlerThread

A handler thread is a subclass of the normal Java thread class. A handler thread is a long-running thread that grabs work from the queue and operates on it. It's a combination of other Android primitives, namely:

Looper : Keeps the thread alive and holds the message queue

MessageQueue : Class holding the list of messages to be dispatched by a Looper

Handler : allows us to send and process message objects associated with a thread's MessageQueue

This means we can keep it running in the background and feed it with more and more packages of work sequentially one after the other until we quit it. HandlerThreads run outside of your activity's life cycle, so they need to be cleaned up properly or else you'll have thread leaks.

There are two main ways to create handler threads.

  1. Create a new handler thread, and get the looper. Now, create a new handler by assigning the looper of the created handler thread and post your tasks on this handler.
  2. Extend the handler thread by creating the CustomHandlerThread class. Then, create a handler to process the task. You'd take this approach if you knew the task you wanted to perform and just needed to pass in the parameters. An example would be like creating a custom HandlerThread class to download images or to perform networking tasks.
            HandlerThread handlerThread = new HandlerThread("TesHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
handler.post(new Runnable(){…});

When we create a handler thread, don't forget to set its priority because CPU can handle only a small number of threads in parallel, so setting the priority can help the system know the right way to schedule this work while other threads are fighting for attention.

Note: Call handlerThread.quit() when you're done with the background thread or on your activity's onDestroy() method.

We can post updates to the UI thread by using local broadcast or by creating a handler with the main looper.

            Handler mainHandler = new Handler(context.getMainLooper());              

mainHandler.post(myRunnable);

When to use handler threads

Handler threads are the perfect solution for the long-running background work that doesn't need UI updates.

ThreadPoolExecutor

What is a thread pool?

A thread pool is basically a pool of threads waiting to be given a task. The tasks that are assigned to these threads will run in parallel. As the tasks execute in parallel, we may want to ensure our code is thread-safe. A thread pool mainly addresses two problems:

  • Improved performance when executing a large collection of asynchronous tasks due to reduced per-task overhead
  • A means of bounding and managing resources (including threads) when executing a collection of tasks

Let's consider an example: If we got 40BMP to decode, where each bitmap takes 4ms to decode, and if we do it on a single thread, it takes over 160ms to decode all bitmaps.

However, if we do it with 10 threads, each decodes four bitmaps. Thus, the time taken to decode these 40 bitmaps would be only 16ms.

The problem here is how to pass the work to each thread, how to schedule that work, and how to manage those threads. It's a very big problem. This is the place where ThreadPoolExecutor comes into the play.

What is ThreadPoolExecutor?

A ThreadPoolExecutor is a class that extends AbstractExecutorService. ThreadPoolExecutor takes care of all the threads.

  • It assigns tasks to threads
  • It keeps them alive
  • It terminates the threads

The way it works under the hood is that tasks to be run are maintained in a work queue. From the work queue, a task is assigned to a thread whenever a thread in the pool becomes free or available.

Runnable

It's an interface that's to be implemented by a class whose instances are intended to be executed by a thread. Simply put: It's a command or a task that can be executed. It's frequently used to run code in a different thread.

            Runnable mRunnable = new Runnable() {
@Override
public void run() {
// Do some work
}
};

Executor

An Executor is an interface used to decouple task submission from task execution. It's an object that executes Runnable.

            Executor mExecutor = Executors.newSingleThreadExecutor(); mExecutor.execute(mRunnable);          

ExecutorService

An Executor that manages asynchronous tasks.

            ExecutorService mExecutorService = Executors.newFixedThreadPool(10); mExecutorService.execute(mRunnable);          

ThreadPoolExecutor

An ExecutorService that assigns tasks to a pool of threads.

More threads aren't always good because the CPU can only execute a certain number of threads in parallel. Once we exceed that number, the CPU has to make some expensive calculations to decide which thread should get assigned based on priority.

While creating the instance of ThreadPoolExecutor, we can specify the number of initial threads and the number of maximum threads. As the workload in the thread pool changes, it'll scale the number of live threads to match.

It's usually recommended to allocate threads based on the number of available cores. This can be achieved by:

            int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();          

Note: This doesn't necessarily return the actual number of physical cores on the device. It's possible CPU may deactivate some cores to save battery, etc.

            ThreadPoolExecutor(
int corePoolSize, // Initial pool size
int maximumPoolSize, // Max pool size
long keepAliveTime, // Time idle thread waits before terminating
TimeUnit unit // Sets the Time Unit for keepAliveTime
BlockingQueue<Runnable> workQueue) // Work Queue

What are these parameters?

  1. corePoolSize: The minimum number of threads to keep in the pool. Initially, there are zero threads in the pool. But as tasks are added to the queue, new threads are created. If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  2. maximumPoolSize: The maximum number of threads allowed in the pool. If this exceeds the corePoolSize and the current number of threads is >= corePoolSize, then new worker threads will be created only if the queue is full.
  3. keepAliveTime: When the number of threads is greater than the core, the noncore threads (excess idle threads) will wait for a new task, and if they don't get one within the time defined by this parameter, they'll terminate.
  4. unit: The unit of time for keepAliveTime.
  5. workQueue: The task queue, which will only hold runnable tasks. It'll have to be a BlockingQueue.

When to use ThreadPoolExecutor

ThreadPoolExecutor is a powerful task-execution framework we can use when a large number of tasks need to be executed parallelly — as it supports task addition in a queue, task cancellation, and task prioritization.

IntentService

IntentService is a subclass inherited from Service. To know about IntentService, we need to know about Service.

Service is a very important component in Android programming. Sometimes we might have a task to be performed even after the application is closed. This is the scenario in which Service will be more helpful. Service can be invoked and canceled by startService ()/stopService () and runs in the background for a long time. It can also be canceled by calling stopSelf() inside of it.

Let's see some overridden methods that'll be helpful for performing operations

onCreate(): Will be called only once until it's stopped.

onStartCommand(): This function will be called after onCreate () for the first time, but it'll be called directly when any component calls startService() with the intent from the second time onwards.

onDestroy(): It's invoked when we stop the service.

The normal flow of service is:

            onCreate() -> onStartCommand() -> onDestroy()          

Returning to IntentService, the Service is started the same way as a normal Service (by calling startService() from the main thread). It handles each intent in onHandleIntent(), rather than onStartCommand(). It uses a worker thread and stops itself when it runs out of work. To use it, we need to extend IntentService and implement onHandleIntent().

Note: IntentService runs on a single worker thread, while Service runs on the main thread. Only one request will be processed at a time.

IntentService is subject to all the background execution limits imposed with Android 8.0 (API level 26). In most cases, you are better off using JobIntentService, which uses jobs instead of services when running on Android 8.0 or higher.

When to use IntentService

IntentService handles asynchronous requests on demand. This is the best option if you don't require that your service handle multiple requests concurrently.

Conclusion

That's all for now. Hope you learned something from this piece. I'll be writing separate pieces for each component specified above. Thanks for reading.

References

"IntentService" — Android Developers documentation

"Understanding Android Threading" — Android Performance Patterns Season 5, Ep. 2

How To Create New Thread In Android

Source: https://betterprogramming.pub/threading-in-android-129b8688436a

Posted by: furnesswidefirearm.blogspot.com

0 Response to "How To Create New Thread In Android"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel