Background processing with AsyncTask

Updated

Android AsyncTask is one the key elements of the whole platform. The class provides easy to understand interface for background threads. Background threads are important in Android since you cannot do anything CPU intensive on the UI thread. If you do, you’ll make the UI very sluggish and at some point the operating system intervenes and displays an Application Not Responding (ANR) dialog to the user and stops the applications.

So in order to do any heavy lifting you need to use background threads and AsyncTask class gives you easy access to them. Google documentation gives you a thorough explanation about the class but I’ll go over the main points here and shown an example on AsyncTask usage.

Basics

AsyncTask class consists of four main methods: onPreExecute(), doInBackground(), onProgressUpdate() and onPostExecute(). Out of these four methods doInBackground() is the only one executed in the background thread. What this means is that you cannot change UI directly from doInBackground and you need to use publishProgress method which calls onProgressUpdate() behind the scenes. So in normal case the AsyncTask program flow goes something like this:

  1. onPreExecute()
  2. doInBackground(Params…)
  3. onProgressUpdate(Progress…)
  4. Do some more work and update UI
  5. onPostExecute(Result)

Example

The following example shows AsyncTask usage in more detail.

Resources

res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">AsyncTask Tutorial</string>

    <string name="start_button">Start AsyncTask</string>
    <string name="ok_button">OK</string>

</resources>

res/layout/activity_asynctask.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".AsyncTaskActivity">

    <Button
        android:id="@+id/startAsyncTask"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onStartButtonClick"
        android:text="@string/start_button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Code

src/com/caphal/android/asynctask/AsyncTaskActivity.java:

package com.tanelikorri.android.asynctask;

import android.app.AlertDialog.Builder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

public class AsyncTaskActivity extends AppCompatActivity {

    private static final String TAG = "AsyncTaskActivity";

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

    /**
     * Start button click handler
     *
     * @param view
     */
    public void onStartButtonClick(View view) {
        // Start new AsyncTask with a parameter
        new PrimeTask().execute(5000);
    }

    /**
     * Calculates given parameter amount of prime numbers starting from 0 and
     * returns the calculation time.
     */
    private class PrimeTask extends AsyncTask<Integer, Integer, Long> {

        private int primeCount;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            Log.d(TAG, "UI thread onPreExecute");
        }

        @Override
        protected Long doInBackground(Integer... params) {

            primeCount = params[0];

            long startTime = System.currentTimeMillis();

            int primesFound = 0;

            for (int i = 2; primesFound < primeCount; i++) {
                if (isPrime(i)) {
                    primesFound++;
                    // Publish progress, calls onProgressUpdate behind the scenes
                    publishProgress(i, (int) (((float) primesFound / (float) primeCount) * 100f));
                }
            }

            long endTime = System.currentTimeMillis();

            return endTime - startTime;
        }

        /**
         * Checks if the given parameter is a prime number. Return true or false
         *
         * @param number Number to test
         * @return True if the parameter is prime number, false otherwise
         */
        private boolean isPrime(int number) {
            for (int i = number - 1; i > 1; i--) {
                if (number % i == 0) {
                    return false;
                }
            }

            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            // Print progress to the log
            Log.d(TAG, values[0] + " is prime " + values[1] + " % ready");
        }

        @Override
        protected void onPostExecute(Long result) {
            super.onPostExecute(result);

            Log.d(TAG, "onPostExecute");

            // Show AlertDialog to user
            Builder builder = new Builder(AsyncTaskActivity.this);
            builder.setMessage(primeCount + " prime(s) found in " + result
                    / 1000 + " second(s).");
            builder.setPositiveButton(R.string.ok_button, null);
            builder.create().show();
        }

    }

}

Screenshots

Main activity After AsyncTask

Source code

Source code for this example project is available here

Further Reading

Comments

Did you find this article helpful? Or are there parts that need some improving? Write your comment below and let me know what you think!

comments powered by Disqus