Android AsyncTasks Parallel Execution

Filed Under: Android

In this tutorial, we’ll be digging deep into AsyncTasks in android by discussing the different ways of executing it. We’ll see the different ways to handle configuration changes with Async Tasks too.
To know the basics of AsyncTasks, we recommend you to read this tutorial.

Android AsyncTasks

AsyncTasks is a helper class that lets us run background tasks which can communicate back with the UI thread.

Following are the main methods provided by the AsyncTask class:

  • onPreExecute
  • doInBackGround
  • onPostExecute
  • onProgressUpdate – In order to invoke this, call the publishProgress method from the doInBackground method.

The syntax to declare an AsyncTask is:


private static class MyAsyncTask extends AsyncTask<A, B, C>

Three types of arguments are passed :

  • A – These are the type of params that can be passed in the execute() method. They are used in the doInBackground.
  • B – This type is available in the onProgressUpdate
  • C – This is the type present in the onPostExecute method.

execute method is invoked on the instance to start the AsyncTask typically.

AsyncTasks should be declared static. Otherwise, leaks would occur since non-static classes contain direct references to the Activities which can prevent the Activity from getting garbage collected if its closed while the AsyncTask is still running.

AsyncTasks Parellel Execution

Till Android Donut, Parallel Execution was not possible when multiple AsyncTasks were executed simultaneously.

From Android Donut multiple async tasks were executed in parallel until Android Honeycomb 3.0 arrived.

For Android Honeycomb, multiple AsyncTasks would be executed serially on a single thread in order to prevent IllegalStateExceptions.

In order to do parallel Execution since Android 3.0, we need to call the method, executeOnExecutor(java.util.concurrent.Executor, Object[]) on the AsyncTask.

Here a THREAD_POOL_EXECUTOR is passed for the Executor.

Note: AsyncTask is built using Java’s Executor Service

How many AsyncTasks are executed in parallel?

It depends on:
Core Pool Size (CORE_POOL_SIZE) (# of threads in the thread pool) = CPU Count + 1

Maximum Pool Size (MAXIMUM_POOL_SIZE) (max # of thread in the thread pool) = CPU Count * 2 +1

So for a smartphone with 4 core processor, 4*2 + 1 = 9 AsyncTasks can be run in parallel.

AsyncTasks Memory Leaks And Orientation Changes

Whenever you define an AsyncTask in your code, the IDE does ask you to set the class as static inner class or memory leaks might occur. Why so?

Non-static inner classes hold a direct reference to the parent class. So if an AsyncTask lives longer than the Activity, it will prevent the Activity from getting garbage collected.

Thus, we can define the AsyncTask as static or keep a WeakReference to the Activity in it.

A static object’s lifecycle is bound to the class it is contained in.

An orientation change can cause IllegalStateException since the enclosed activities configuration changes while the AsyncTask is running in the background. This can lead to memory leaks.

So setting android:configChanges = “orientation” is one way to deal with AsyncTask and Screen rotation.
But this isn’t a good practice.

Another possible way is by locking the orientation to the current one in the onPreExecute and unlocking in the onPostExecute after the background work is over.

The ideal way is to put an AsyncTask inside a Fragment and setRetainInstance(true).

For the sake of simplicity and since we are focusing on Parallel Execution in this tutorial, we’ll set the configChanges in the AndroidManifest.xml file in our below project code.

Project Structure

android multiasynctask project structure

Code

The code for the activity_main.xml layout is given below:


<?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:layout_margin="16dp"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10" />


    <ProgressBar
        android:id="@+id/progressBar2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10" />


    <Button
        android:id="@+id/btnSerialExecution"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SERIAL EXECUTION" />


    <ProgressBar
        android:id="@+id/progressBar3"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10" />


    <ProgressBar
        android:id="@+id/progressBar4"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10" />


    <Button
        android:id="@+id/btnParallelExecution"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PARELLEL EXECUTION" />

</LinearLayout>

We’ve created 4 ProgressBar, 2 will be filled serially using AsyncTask progress updates and the remaining two would be executed parallelly. Let’s see the code for it.


package com.journaldev.androidmultiasynctasks;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    Button btnParallelExecution, btnSerialExecution;
    ProgressBar progressBar1, progressBar2, progressBar3, progressBar4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
    }

    private void initViews() {
        btnSerialExecution = findViewById(R.id.btnSerialExecution);
        progressBar1 = findViewById(R.id.progressBar1);
        progressBar2 = findViewById(R.id.progressBar2);
        btnParallelExecution = findViewById(R.id.btnParallelExecution);
        progressBar3 = findViewById(R.id.progressBar3);
        progressBar4 = findViewById(R.id.progressBar4);

        btnSerialExecution.setOnClickListener(this);
        btnParallelExecution.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {

        switch (view.getId()) {
            case R.id.btnSerialExecution:

                MyObject myObject = new MyObject(progressBar1, 2);

                MyAsyncTask aTask1 = new MyAsyncTask();
                aTask1.execute(myObject);

                myObject = new MyObject(progressBar2, 3);

                MyAsyncTask aTask2 = new MyAsyncTask();
                aTask2.execute(myObject);
                break;

            case R.id.btnParallelExecution:

                myObject = new MyObject(progressBar3, 2);

                MyAsyncTask aTask3 = new MyAsyncTask();
                aTask3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, myObject);

                myObject = new MyObject(progressBar4, 3);

                MyAsyncTask aTask4 = new MyAsyncTask();
                aTask4.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, myObject);
                break;
        }

    }

    private static class MyAsyncTask extends AsyncTask<MyObject, Integer, Void> {

        private WeakReference<MyObject> myObjectWeakReference;

        @Override
        protected Void doInBackground(MyObject... params) {
            this.myObjectWeakReference = new WeakReference<>(params[0]);
            for (int i = 0; i <= 10; i++) {
                publishProgress(i);

                try {
                    Thread.sleep(myObjectWeakReference.get().interval * 100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }


        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            myObjectWeakReference.get().progressBar.setProgress(values[0]);
        }
    }

    private class MyObject {
        private ProgressBar progressBar;
        private int interval;

        MyObject(ProgressBar progressBar, int interval) {
            this.progressBar = progressBar;
            this.interval = interval;
        }
    }
}

We’ve created a Model class which is passed in the AsyncTask param.
It contains the ProgressBar instance and the interval for which the Thread would sleep.

The output of the above application in action is given below:

android multiasynctask output

This brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Shubham Yadav says:

    You said “Another possible way is by locking the orientation to the current one in the onPreExecute and unlocking in the onPostExecute after the background work is over.”. I know how to lock the orientation but how do I unlock it, can you please explain?

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages