Android Tutorial: Learning how to handling the Handler

One of the most interesting and useful types in the Android SDK is the Handler class. Most of the time, it is used to process messages and schedule a task to run at a future time.

This article, explains what the class is good for and offers examples. Previous article: Android Tutorial: Camera API

Overview

The android.os.Handler class is an exciting utility class that, among others, can be scheduled do execute a Runnable at a future time. Any task assigned to a Handler will run on the Handler’s thread. In turn, the Handlerruns on the thread that created it, which in most cases would be the UI thread. As such, you should not schedule a long-running task with a Handler because it would make your application freeze. However, you can use a Handler to handle a long-running task if you can be split the task into smaller parts, which you learn how to achieve in this section.

Android Handler

To schedule a task to run at a future time, call the Handler class’s postDelayed or postAtTime method.

public final boolean postDelayed(Runnable task, long x)


public final boolean postAtTime(Runnable task, long time) postDelayed runs a task x milliseconds after the method is called. For example, if you want a Runnable to start five seconds from now, use this code.

Handler handler = new Handler();


handler.postDelayed(runnable, 5000);

postAtTime runs a task at a certain time in the future. For example, if you want a task to run six seconds later, write this.

Handler handler = new Handler();
handler.postAtTime(runnable, 6000 + System.currentTimeMillis());

Example

As an example, consider the HandlerDemo project that uses Handler to animate an ImageView. The animation performed is simple: show an image for 400 milliseconds, then hide it for 400 milliseconds, and repeat this five times. The entire task would take about four seconds if all the work is done in a for loop that sleeps for 400 milliseconds at each iteration. Using the Handler, however, you can split this into 10 smaller parts that each takes less than one millisecond (the exact time would depend on the device running it). The UI thread is released during each 400ms wait so that it can cater for something else.

Note

Android offers animation APIs that you should use for all animation tasks. This example uses Handler to animate a control simply to illustrate the use of Handler.

Listing 1.1 shows the manifest (the AndroidManifest.xml file) for the project.

Listing 1.1: The manifest for HandlerDemo

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.example.handlerdemo"
    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=".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>

Nothing spectacular in the manifest. It shows that there is one activity named MainActivity. The layout file for the activity is given in Listing 1.2.

Listing 1.2: The res/layout/activity_main.xml file in HandlerTest

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity" > 

    <ImageView 
        android:id="@+id/imageView1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_alignParentLeft="true" 
        android:layout_alignParentTop="true" 
        android:layout_marginLeft="51dp" 
        android:layout_marginTop="58dp" 
        android:src="@drawable/surprise" /> 

    <Button 
        android:id="@+id/button1" 
        style="?android:attr/buttonStyleSmall" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_alignRight="@+id/imageView1" 
        android:layout_below="@+id/imageView1" 
        android:layout_marginRight="18dp" 
        android:layout_marginTop="65dp" 
        android:onClick="buttonClicked" 
        android:text="Button"/>
</RelativeLayout>

The main layout for MainActivity is a RelativeLayout that contains an ImageView to be animated and a button to start animation.

Now look at the MainActivity class in Listing 1.3. This is the main core of the application.

Listing 1.3: The MainActivity class in HandlerDemo

package com.example.handlerdemo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {

    int counter = 0;
    Handler handler = new Handler(); 

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

    @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 buttonClicked(View view) {
        counter = 0;
        getUserAttention();
    }

    private void getUserAttention() {
        handler.post(task);
    }
 
    Runnable task = new Runnable() {
        @Override
        public void run() {
            ImageView imageView = (ImageView)
                    findViewById(R.id.imageView1); 
            if (counter % 2 == 0) {
                imageView.setVisibility(View.INVISIBLE); 
            } else { 
                imageView.setVisibility(View.VISIBLE); 
            } 
            counter++; 
            if (counter < 8) { 
                handler.postDelayed(this, 400); 
            } 
        } 
    }; 
}

The brain of this activity is a Runnable called task, which animates the ImageView, and the getUserAttention method that calls the postDelayed method on a Handler. The Runnable sets the ImageView’s visibility to Visible or Invisible depending on whether the value of the counter variable is odd or even.

If you run the HandlerDemo project, you’ll see something similar to the screenshot in Figure 1.1. Note how the ImageView flashes to get your attention. Try clicking the button several times quickly to make the image flash faster. Can you explain why it goes faster as you click?

Figure 1.1: The HandlerTest application

Summary

In this article, you learned about the Handler class and write an application that makes use of the class.

Leave a Reply

Your email address will not be published.