In this article, we will talk about what is thread and how to create a separate worker thread in our android application to achieve asynchronous processing with a basic example.
Thread
Android follows a multitasking design. This allows a number of applications to run at the same time. Let’s say you are playing a song in Ganna app and you are typing something on WhatsApp at the same time. You are using two tasks at the same time which we called as multitasking. But let’s say if we talk about one task/process/App i.e WhatsApp then in WhatsApp, parallelly while you are typing a word at the same time it is showing some words related to what you are typing and this is achieved by making use of threads.
A thread is a lightweight subprocess, the smallest unit of processing. It is a separate path of execution.
Threads are independent. If there occurs exception in one thread, it doesn’t affect other threads.
As shown in the above figure, At runtime, an Android operating system is always hosting an app as a process. So each app gets a dedicated process to run itself. At any point in time, it can host any no. of apps and for each app, you have a separate process. If you consider one particular process then there can be any no. of threads inside it and one of the thread is Main thread (default thread comes with every application) or also referred to as UI thread. In this thread, all of your UI tasks are getting executed like rendering a button, click of a button, everything that is related to UI happens in this particular thread.
Android supports the usage of the Thread class to perform asynchronous processing. Thread class provides constructors and methods to create and perform operations on a thread. Thread class extends Object class and implements Runnable interface.
Always remember the two most important rules when working with threads:
- Don’t block the Main thread
- Don’t try and access the UI directly from the worker thread
Whats the need of a thread or why we use Threads?
- To perform asynchronous or background processing
- Increases the responsiveness of GUI applications
- Take advantage of multiprocessor systems
- Simplify program logic when there are multiple independent entities
The Application Main Thread
All apps, when started, run in a single Main thread. It’s also known as the User Interface (or UI) thread.
Android modifies the user interface and handles input events from this main thread. Android collects all events in this thread in a queue and processes this queue with an instance of the Looper
class.
By default, all components (Activities, Services, Content provider, Broadcast receivers…) of the same application run in the same process and thread (called the “main” thread).
When we try to execute long-running operations in our application by default they are called in the main thread and are called synchronously, which means that the UI will remain completely unresponsive until the operation completes. Running long operations on the app’s main thread will freeze the app’s user interface and after a couple seconds, we get an “application not responding” dialog message with the option to force quit the app.
For example, let us consider the below code:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startThread(View view) { for (int i = 1; i <= 10; i++) { Log.d(TAG, "startThread: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
In the above code on clicking the start button, the startThread method will be called. This method will print the log statement after every one second, ten times.
By default, this method will be called inside the main thread, synchronously and it will freeze the app until the operation is finished. So in between, if we will try to interact with other UI events (on/off switch) we get an “application not responding” dialog message with the options close app and wait like this:
What triggers the ANR dialog?
- If an app can’t respond to user input within 5 seconds
- If Broadcast receivers haven’t finished executing within 10 seconds
To avoid this we have to move heavy operations like Network operations and database calls, as well as loading of certain components onto a separate thread. A separate thread will avoid blocking the UI while they are being performed (i.e., they are performed asynchronously from the UI).
Creating New Thread
Thread implementation can be achieved in two ways:
- Extending the Thread class
- Implementing the Runnable Interface
1) By extending Thread class
- The class should extend the Thread class.
- The class should override the run() method.
- The functionality that is expected by the Thread to be executed is written in the run() method.
- To start the thread, create a new Thread object and then call start().
void start(): Creates a new thread and makes it runnable.
void run(): The new thread begins its life inside this method.
Example:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startThread(View view) { ExampleThread thread = new ExampleThread(10); thread.start(); } class ExampleThread extends Thread { int seconds; ExampleThread(int seconds) { this.seconds = seconds; } @Override public void run() { for (int i = 1; i <= seconds; i++) { Log.d(TAG, "startThread: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
Output:
2) By implementing Runnable interface
- The class should implement the Runnable interface
- The class should override the run() method.
- The functionality that is expected by the Thread to be executed is put in the run() method
- To start the thread, pass the runnable to a new Thread object and then call start().
Example:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startThread(View view) { ExampleRunnable runnable = new ExampleRunnable(10); new Thread(runnable).start(); } class ExampleRunnable implements Runnable { int seconds; ExampleRunnable(int seconds) { this.seconds = seconds; } @Override public void run() { for (int i = 1; i <= seconds; i++) { Log.d(TAG, "startThread: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
Extends Thread class vs Implements Runnable Interface
- Extending the Thread class will make your class unable to extend other classes, because of the single inheritance feature in JAVA. However, this will give you a simpler code structure. If you implement Runnable, you can gain better object-oriented design and consistency and also avoid the single inheritance problems.
- If you just want to achieve basic functionality of a thread you can simply implement Runnable interface and override run() method. But if you want to do something serious with thread object as it has other methods like suspend(), resume(), ..etc which are not available in Runnable interface then you may prefer to extend the Thread class.
Worker threads
According to the single-threaded model described above, it’s important to the responsiveness of your application’s UI that you do not block the UI thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads (“background” or “worker” threads).
The other rule says, that you cannot update the UI from any thread other than the UI thread or the “main” thread.
For example:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; Button btn_start; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_start = findViewById(R.id.btn_start); } public void startThread(View view) { ExampleThread thread = new ExampleThread(10); thread.start(); } class ExampleThread extends Thread { int seconds; ExampleThread(int seconds) { this.seconds = seconds; } @Override public void run() { for (int i = 1; i <= seconds; i++) { if (i == 5) { btn_start.setText("50%"); } Log.d(TAG, "startThread: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
In the above example, the ExampleThread will execute the log statement up to i equals to 4 and when i equals to 5 our app will crash.
In logcat, you can see the error message : Only the original thread that created a view hierarchy can touch its views.It clearly says that you cannot update the UI from any thread other than the UI thread.
To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
For example, by using View.post(Runnable) you can access the UI Thread inside other Thread(i.e ExampleThread) as shown below:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; Button btn_start; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_start = findViewById(R.id.btn_start); } public void startThread(View view) { ExampleThread thread = new ExampleThread(10); thread.start(); } class ExampleThread extends Thread { int seconds; ExampleThread(int seconds) { this.seconds = seconds; } @Override public void run() { for (int i = 1; i <= seconds; i++) { if (i == 5) { btn_start.post(new Runnable() { @Override public void run() { btn_start.setText("50%"); } }); } Log.d(TAG, "startThread: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Thread Basic Example
Let’s create a new android application to understand the concept of asynchronous processing in Android applications.
- Create a new android application Threadexample.
- Open layout file activity_main and write the below code. This file consists of one Imageview and one button.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:context=".MainActivity"> <ImageView android:id="@+id/image" android:layout_width="350dp" android:layout_height="350dp" android:background="@drawable/border_image"/> <Button android:id="@+id/btn_download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:background="@android:color/holo_blue_bright" android:onClick="downloadImage" android:padding="10dp" android:text="download image" android:textColor="@android:color/black" android:textStyle="bold" /> </LinearLayout>
3 . Open MainActivity.java and write the below code:
MainActivity.java
package trendlife.threadexample; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class MainActivity extends AppCompatActivity { ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.image); } public void downloadImage(View view) { Log.i("Button", "Tapped"); DownloadImage downloadImage = new DownloadImage(this); new Thread(downloadImage).start(); } class DownloadImage implements Runnable { Activity activity; Bitmap result = null; DownloadImage(Activity activity) { this.activity = activity; } @Override public void run() { URL url; HttpURLConnection httpURLConnection; try { url = new URL("https://vignette.wikia.nocookie.net/disney/images/0/0a/ElsaPose.png/revision/latest?cb=20170221004839"); httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.connect(); InputStream in = httpURLConnection.getInputStream(); result = BitmapFactory.decodeStream(in); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } activity.runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(result); Toast.makeText(activity, "Image downloaded", Toast.LENGTH_SHORT).show(); } }); } } }
4 . Inside MainActivity, on DOWNLOAD IMAGE button click downloadImage() method will call. This method starts a new thread DownloadImage.
5 . DownloadImage Thread downloading an image over the network and displaying it in an ImageView and then display a toast message Image Downloaded.
6 . Open AndroidManifest file and add INTERNET permission to download an image over the network.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
7 . On running the android application Threadexample it will look like this as shown below:
I hope this article will help you in understanding what is thread, why we use thread and how to use thread in our android applications. As we know it is possible to access UI components inside the worker thread using some of the methods which we have discussed above but there is one more alternative in android i.e Handler which also allows you communicate back with the UI thread from other background thread. In the next article, we will talk about Handler in brief.