Asynchronous Tasks - Android Application Development: A Beginner's Tutorial (2015)

Android Application Development: A Beginner's Tutorial (2015)

Chapter 23. Asynchronous Tasks

This chapter talks about asynchronous tasks and explains how to handle them using the AsyncTask class. It also presents a photo editor application that illustrates how this class should be used.

Overview

The android.os.AsyncTask class is a utility class that makes it easy to handle background processes and publish progress updates on the UI thread. This class is meant for short operations that last at most a few seconds. For long-running background tasks, you should use the Java Concurrency Utilities framework.

The AsyncTask class comes with a set of public methods and a set of protected methods. The public methods are for executing and canceling its task. The execute method starts an asynchronous operation and cancel cancels it. The protected methods are for you to override in a subclass. The doInBackground method, a protected method, is the most important method in this class and provides the logic for the asynchronous operation.

There is also a publishProgress method, also a protected method, which is normally called multiple times from doInBackground. Typically, you will write code to update a progress bar or some other UI component here.

Then there are two onCancelled methods for you to write what should happen if the operation was canceled (i.e. if the AsyncTask’s cancel method was called).

Example

As an example, the PhotoEditor application that accompanies this book uses the AsyncTask class to perform image operations that each takes a few seconds. AsyncTask is used so as not to jam the UI thread. Two image operations, invert and blur, are supported.

The application manifest (the AndroidManifest.xml file) is printed in Listing 23.1.

Listing 23.1: The manifest for PhotoEditor

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.photoeditor"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="17" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name="com.example.photoeditor.MainActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

The layout file, printed in Listing 23.2, shows that the application uses a vertical LinearLayout to house an ImageView, a ProgressBar, and two buttons. The latter are contained in a horizontal LinearLayout. The first button is used to start the blur operation and the second to start the invert operation.

Listing 23.2: The res/layout/activity_main.xml file in PhotoEditor

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

android:paddingLeft="16dp"

android:paddingRight="16dp" >

<LinearLayout

android:layout_height="wrap_content"

android:layout_width="fill_parent"

android:orientation="horizontal" >

<Button

android:id="@+id/blurButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="doBlur"

android:text="@string/blur_button_text" />

<Button

android:id="@+id/button2"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:onClick="doInvert"

android:text="@string/invert_button_text" />

</LinearLayout>

<ProgressBar

android:id="@+id/progressBar1"

style="?android:attr/progressBarStyleHorizontal"

android:layout_width="fill_parent"

android:layout_height="10dp" />

<ImageView

android:id="@+id/imageView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="top|center"

android:src="@drawable/photo1" />

</LinearLayout>

Finally, the MainActivity class for this project is given in Listing 23.3.

Listing 23.3: The MainActivity class in PhotoEditor

package com.example.photoeditor;

import android.app.Activity;

import android.graphics.Bitmap;

import android.graphics.drawable.BitmapDrawable;

import android.os.AsyncTask;

import android.os.Bundle;

import android.view.Menu;

import android.view.View;

import android.widget.ImageView;

import android.widget.ProgressBar;

public class MainActivity extends Activity {

private ProgressBar progressBar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

progressBar = (ProgressBar) findViewById(R.id.progressBar1);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it

// is present.

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

public void doBlur(View view) {

BlurImageTask task = new BlurImageTask();

ImageView imageView = (ImageView)

findViewById(R.id.imageView1);

Bitmap bitmap = ((BitmapDrawable)

imageView.getDrawable()).getBitmap();

task.execute(bitmap);

}

public void doInvert(View view) {

InvertImageTask task = new InvertImageTask();

ImageView imageView = (ImageView)

findViewById(R.id.imageView1);

Bitmap bitmap = ((BitmapDrawable)

imageView.getDrawable()).getBitmap();

task.execute(bitmap);

}

private class InvertImageTask extends AsyncTask<Bitmap, Integer,

Bitmap> {

protected Bitmap doInBackground(Bitmap... bitmap) {

Bitmap input = bitmap[0];

Bitmap result = input.copy(input.getConfig(),

/*isMutable'*/true);

int width = input.getWidth();

int height = input.getHeight();

for (int i = 0; i < height; i++) {

for (int j = 0; j < width; j++) {

int pixel = input.getPixel(j, i);

int a = pixel & 0xff000000;

a = a | (~pixel & 0x00ffffff);

result.setPixel(j, i, a);

}

int progress = (int) (100*(i+1)/height);

publishProgress(progress);

}

return result;

}

protected void onProgressUpdate(Integer... values) {

progressBar.setProgress(values[0]);

}

protected void onPostExecute(Bitmap result) {

ImageView imageView = (ImageView)

findViewById(R.id.imageView1);

imageView.setImageBitmap(result);

progressBar.setProgress(0);

}

}

private class BlurImageTask extends AsyncTask<Bitmap, Integer,

Bitmap> {

protected Bitmap doInBackground(Bitmap... bitmap) {

Bitmap input = bitmap[0];

Bitmap result = input.copy(input.getConfig(),

/*isMutable=*/ true);

int width = bitmap[0].getWidth();

int height = bitmap[0].getHeight();

int level = 7;

for (int i = 0; i < height; i++) {

for (int j = 0; j < width; j++) {

int pixel = bitmap[0].getPixel(j, i);

int a = pixel & 0xff000000;

int r = (pixel >> 16) & 0xff;

int g = (pixel >> 8) & 0xff;

int b = pixel & 0xff;

r = (r+level)/2;

g = (g+level)/2;

b = (b+level)/2;

int gray = a | (r << 16) | (g << 8) | b;

result.setPixel(j, i, gray);

}

int progress = (int) (100*(i+1)/height);

publishProgress(progress);

}

return result;

}

protected void onProgressUpdate(Integer... values) {

progressBar.setProgress(values[0]);

}

protected void onPostExecute(Bitmap result) {

ImageView imageView = (ImageView)

findViewById(R.id.imageView1);

imageView.setImageBitmap(result);

progressBar.setProgress(0);

}

}

}

The MainActivity class contains two private classes, InvertImageTask and BlurImageTask, which extend AsyncTask. The InvertImageTask task is executed when the Invert button is clicked and the BlurImageTask when the Blur button is clicked.

The doInBackground method in each task processes the ImageView bitmap in a for loop. At each iteration it calls the publishProgress method to update the progress bar.

Figure 23.1 shows the initial bitmap and Figure 23.2 shows the bitmap after an invert operation.

image

Figure 23.1: The ImageEditor application

image

Figure 23.2: The bitmap after invert

Summary

In this chapter you learned to use the AsyncTask class and created a photo editor application that uses it.