This tutorial describes the usage of Threads and Handlers in your application. It also covers how to handle the application lifecycle together with threads.
Threads
When an application is launched, the system creates a thread of execution for the application, called "main".
This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also the thread in which your application interacts with components from the Android UI toolkit (components from the android.widget and android.view packages). As such, the main thread is also sometimes called the UI thread.
The system does not create a separate thread for each instance of a component. All components that run in the same process are instantiated in the UI thread, and system calls to each component are dispatched from that thread
When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang.
Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.
Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread - you must do all manipulation to your user interface from the UI thread.
Handler
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it - from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.
In this article we will use first scenario.
Tutorial: Threads & Handler
In this example we use the Handler class to handle a ProgressDialog and ImageView in a background Thread.
We will display a ProgressDialog with the message "Image Downloading" until the Thread finishes its work of image downloading. After downloading the image, if the image is successfully downloaded, we will show it, otherwise we will show an "Error" like image on the screen.
Step 1
Create a new Android project called "Handler" with the following statistics.
Project Build Target: Android 2.3 or higher
Package Name: com.test
Activity Name: HandlerActivity
Step 2
Open your "AandroidManifest" file and add the following code to it.
We need to use the permission "Internet". So add the following line:
<uses-permission android:name="android.permission.INTERNET"/>
Now, your manifest file will look like this.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".HandlerActivity"
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>
Step 3
We need to use an ImageView which shows an image after downloading.
Put an "error" image inside the "drawable" directory. An error image will be shown if we fail to download the image from the internet.
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/imageView1"
/>
</LinearLayout>
Step 4
Now, we will start the coding of the HandlerActivity.
Open your HandlerActivity file, and enter the following code. In the code, I wrote all the relevant information in the comments to help you understand.
HandlerActivity.java
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.widget.ImageView;
public class HandlerActivity extends Activity
{
Handler handler;
ProgressDialog dialog;
Bitmap bitmap;
ImageView imageView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/*take reference of ImageView*/
imageView = (ImageView)findViewById(R.id.imageView1);
/*initialize handler*/
handler = new Handler();
/*create new Progressdialog*/
dialog = new ProgressDialog(this);
/*set title of dialog*/
dialog.setTitle("Processing");
/*set message you want to display to user*/
dialog.setMessage("Downloading Image...");
/*whether dialog disappear on "Back" button press or not*/
dialog.setCancelable(true);
/*finally show dialog on the screen*/
dialog.show();
/*
* create a new thread to download image
* Note : if you want any error, pass wrong image url
*/
MyThread thread = new MyThread("http://www.hcs.harvard.edu/csharp/Logo1.png");
thread.start();
}
public class MyThread extends Thread
{
String url;
public MyThread(String url)
{
this.url=url;
}
@Override
public void run()
{
try
{
/*provides convenience methods to access request properties */
HttpUriRequest request = new HttpGet(url);
/*Interface for an HTTP client*/
HttpClient httpClient = new DefaultHttpClient();
/*To handle response of HttpClient Request*/
HttpResponse response;
response = httpClient.execute(request);
/*Represents a status line as returned from a HTTP server*/
StatusLine statusLine = response.getStatusLine();
/*take status code of response*/
int statusCode = statusLine.getStatusCode();
//If Successful
if (statusCode == 200)
{
/*entity that received with an HTTP message*/
HttpEntity entity = response.getEntity();
/*get bytes from entity*/
byte[] bytes = EntityUtils.toByteArray(entity);
/*decode bytes into bitmap*/
bitmap = BitmapFactory.decodeByteArray(bytes, 0,bytes.length);
}
/*if status code is other then 200 means that there is some problem*/
else
{
bitmap=null;
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
/*Causes the Runnable to be added to the message queue*/
handler.post(new Runnable()
{
@Override
public void run()
{
/*if image successfully download*/
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(R.drawable.symbol_error);
/*
* Though we made dialog setCancelable(true), user can
* cancel dialog, but thread will continue running in
* background, so we need to check whether dialog is
* currently showing to the screen or not.
*/
if(dialog.isShowing())
dialog.dismiss();
}
});
}
}
}
Step 5
Right-click your project -> Run -> Run Configuration
Your IP address is your internet address, which you can get using the "ipconfig" command or from the status of your network connection. Your IP address and Port are inside the field excluding the "<" and ">" symbols.
Step 6
Run your application. (You must have a device or emulator or 2.3 API.)
The following is a successful download output:
If you pass the wrong URL of the image:
Summary
In this tutorial, we learn how to use Threads in Android applications and how to integrate with the UI thread.