Threading in Android

Threading in Android
11 November 2023
1
Share

What is Threading in Android

When we write functions methods or any code and want to execute some piece of code it goes to some kind of queue so it is waiting for its turn to execute and that queue is pretty much a thread.

package com.rishiz.app;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(this,"MainActivity Toast",Toast.LENGTH_SHORT).show();

        for (int i=0;i<20;i++){
            Log.d("Output","for loop "+i);
        }

        int x=10;

        while(x<25){
            x++;
            Log.d("Output","while loop "+x);
        }
    }
}

So each piece of code goes into the queue or thread and waits until it’s time to execute.

In the above code first, the setContentView is executed it will set our view and layout and when it is finished toast pop-up will show.

for loop start executed when toast pop-up executed, we can’t have it simultaneously.

After the execution of the for loop while loop is executed.

So you can see that everything in for loop is executed and after it is finished while the loop is executed.

There is no chance to execute a while loop before a for loop that is because the for loop and while are on the same thread and if something is in the thread then it must wait until its turn.

Let’s imagine that for loop is some kind of downloading from the internet and while loop is the user trying to open the home page in that case user can’t able to open the home page until the downloading process is completed.

Therefore to deal with this we are using multithread so that the downloading process can be done on different threads and the user can do their task on the app without blocking.

While executing for loop while loop is blocked by default in Android everything is executed on UI or main thread(UI and main thread are the same thing).

While using Firebase by default the operation like downloading or the operations that block others is executed in the background.

Types of thread

  1. UI Thread (Main Thread):
    • The UI thread is the main thread of an Android application responsible for handling user interface interactions and rendering the UI.
    • It’s where all UI components are created, updated, and interacted with.
    • It should be kept responsive, as any long-running operation on the UI thread can lead to an “Application Not Responding” (ANR) error and a frozen UI.
  2. Background Threads:
    • Background threads are used to perform tasks that are not directly related to the UI, such as network requests, file I/O, database operations, and other time-consuming tasks.
    • Executing long-running operations on the UI thread can cause UI freezes and a poor user experience, so such tasks should be offloaded to background threads.

Multithreading in Android

Multithreading in Android refers to the ability of an Android application to execute multiple threads concurrently. A thread is a unit of execution within a process. Multithreading allows an application to perform multiple tasks concurrently, which can lead to improved performance and responsiveness.

Application Not Responding error

Create two buttons and do the following operations.

package com.rishiz.app;

import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    Button btnClick;
    Button btnDownloading;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnClick = findViewById(R.id.button);
        btnDownloading = findViewById(R.id.button2);

        btnClick.setOnClickListener(v -> {
            Toast.makeText(this, "MainActivity Toast", Toast.LENGTH_SHORT).show();
        });

        btnDownloading.setOnClickListener(v -> {
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Log.d("Output", "for loop " + i);
            }
        });

    }
}

On click of the downloading button, we have to simulate the downloading to achieve this we are using Thread.sleep it will pause the execution on a current thread for the given time.

So you see that the log is printed on waiting of 1 second each time.

The simulated downloading process button seems to be pressed and the user is not able to click the UI process button.

So when we click the button for downloading it is in a pressed state until the downloading process is completed(in our case downloading = execution of for loop)

once the downloading is completed users can able to click on another button or other views.

But if the user does anything while the execution of downloading app will crash with the error “Application Not Responding error” thats because ui thread is blocked.

In Android, by default, everything is executed on the UI thread until we explicitly do not execute this on the background thread.

So to fix this create a new thread to execute the downloading process in the background thread.

new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i=0;i<20;i++){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        Log.d("Output","for loop "+i);
                    }
                }
            }).start();

We are now able to click the UI process button the UI thread is not blocked anymore now because we are simultaneously running two operations, downloading and the clicking button UI process.

Implementation of Threading in Android

  1. Running Code on the UI Thread

To run code on the UI thread, you can use the runOnUiThread method from the Activity class.

runOnUiThread(new Runnable() {
                @Override
                public void run() {
               // code to run on the UI thread     
                }
            });

we’re using the runOnUiThread method to run some code on the UI thread. This method takes a Runnable object as its argument, which contains the code to be executed on the UI thread.

2. Creating a Background Thread

To create a new background thread, you can use the Thread class and pass a Runnable object to its constructor. 

new Thread(new Runnable() {
@Override
public void run() {
// background task
}
}).start();

3. Using Kotlin Coroutines

Kotlin Coroutines is a lightweight framework for performing asynchronous operations in Kotlin. It provides a simple and elegant way to write asynchronous code that is easy to read and maintain.

GlobalScope.launch {
    // background task
}

we’re using the launch function from the GlobalScope object to start a new coroutine that will perform the background task. The launch function returns a Job object, which can be used to cancel the coroutine if needed.

4. Using Handler

The Handler class is used to send and process messages between threads. It’s commonly used to perform delayed or periodic background tasks

val handler = Handler()
handler.postDelayed({
    // background task
}, 1000)

In this example, we’re creating a new Handler object and using its postDelayed method to schedule a background task to be executed after a delay of 1000 milliseconds (1 second). The postDelayed method takes a Runnable object as its first argument, which contains the background task to be executed.

5. Using ExecutorService

ExecutorService is a powerful concurrency framework that allows you to manage a pool of threads and execute tasks concurrently

val executorService = Executors.newFixedThreadPool(4)

executorService.submit {
    // background task
}

executorService.shutdown()

In this example, we’re creating a new ExecutorService object with a fixed pool size of 4 threads. We’re using its submit method to submit a Runnable object that contains the background task we want to perform. Finally, we’re calling the shutdown method to stop the ExecutorService and release its resources.

6. Using IntentService

IntentService is a class in Android that allows you to perform background tasks in a separate thread. It’s commonly used for tasks that need to be performed in the background, but don’t need to run continuously. Here’s an example of how to use it:

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        // background task
    }
}

we’re creating a new MyIntentService class that extends IntentService. We’re overriding the onHandleIntent method to perform the background task. The onHandleIntent method is called on a separate thread, so you don’t need to create a new thread yourself.

7. Using RxJava

RxJava is a popular reactive programming library that allows you to create asynchronous and event-driven programs.

Observable.fromCallable {
    // background task
}.subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe { result ->
    // code to run on the UI thread
}

In this example, we’re using the fromCallable method to create an Observable object that contains the background task we want to perform. We’re using the subscribeOn method to specify that the task should run on the io scheduler, which is optimized for I/O-bound tasks. We’re using the observeOn method to specify that the result should be delivered on the main thread. Finally, we’re using the subscribe method to handle the result.

8. Using HandlerThread

HandlerThread is a class in Android that allows you to create a thread that has a Looper associated with it. This makes it easy to perform tasks on a separate thread and communicate back to the main thread. Here’s an example of how to use it:

class MyHandlerThread : HandlerThread("MyHandlerThread") {
    private lateinit var handler: Handler

    override fun onLooperPrepared() {
        handler = Handler(looper)
    }

    fun postTask(task: Runnable) {
        handler.post(task)
    }
}

we’re creating a new MyHandlerThread class that extends HandlerThread. We’re overriding the onLooperPrepared method to create a new Handler object that’s associated with the thread’s Looper. We’re also defining a postTask method that allows us to post a Runnable object to the thread’s Handler.

To use the MyHandlerThread class, we can create a new instance of the class and call its start method to start the thread:

val myHandlerThread = MyHandlerThread()
myHandlerThread.start()

myHandlerThread.postTask {
    // background task
}

we’re creating a new MyHandlerThread object and starting the thread. We’re using the postTask method to post a Runnable an object that contains the background task we want to perform.

There are various ways to utilize threads in Android Kotlin, each with its benefits and drawbacks. When selecting a concurrency framework, it’s important to consider factors like performance, complexity, and ease of use.