ANDROID THREADS, HANDLERS AND ASYNCTASK - TUTORIAL






ANDROID THREADS, HANDLERS AND ASYNCTASK - TUTORIAL





Lars Vogel







Version 1.9










03.03.2012


























Revision History

Revision 0.1

21.10.2010

Lars

Vogel


Created

Revision 0.2 - 1.9

21.12.2010 - 03.03.2012

Lars

Vogel


bug fixes and updates








Android Threads, Handlers AsyncTask


This tutorial
describes the usage of Threads, Handlers and AsyncTask in your
application. It also covers how to handle the application
lifecycle together with threads. It also describes Traceview to
trace an application for performance problems. It is based on
Eclipse 3.7, Java 1.6 and Android 4.0 (Ice Cream Sandwitch)















1. ANDROID THREADS AND BACKGROUND PROCESSING





Android modifies the user
interface and handles input events from one single user
interface thread
which is
also called the main
thread
.


If the programmer does
not use any concurrency constructs, all code of an Android application runs
in this thread.


If you perform a long
lasting operation, e.g. loading a file or accessing data from the Internet,
the user interface of your Android Application will block until the
corresonding code has finished.


To provide a good user
experience all potentially slow running operations in an Android application
should run asynchronously, e.g. via some way of concurrency constructs of
the Java language or the Android framework. This includes all potential slow
operations, like network, file and database access and complex calculations.


Android enforced that
with an Application
not responding
(ANR) dialog
if an Activity does
not react within 5 seconds to user input. From this dialog the user can
choose to stop the application.









2. ANDROID BASICS





The following assumes
that you have already basic knowledge in Android
development
. Please check the Android
development tutorial
to learn
the basics.









3. BACKGROUND PROCESSING











3.1. THREADS





Android supports the
usage of the Threads class
to perform asynchronous processing.


Android also supplies
the java.util.concurrent package
to perform something in the background, e.g. using the ThreadPools and Executor classes.


If you need to update
the user interface from a new Thread,
you need to synchronize with the user
interface thread
.


You can use the android.os.Handler class
or the AsyncTasks class
for this.









3.2. HANDLER





The Handler class
can update the user interface. A Handler provides
methods for receiving instances of the Message or Runnable class.


To use a handler you
have to subclass it and override the handleMessage() to
process messages. To process a Runnable you
can use the post() method.
You only need one instance of a Handler in
your Activity.


You thread can post
messages via the sendMessage(Message
msg)
method or via thesendEmptyMessage() method.









3.3. ASYNCTASK





The AsyncTask class
encapsulates the creation of Threads and Handlers. An AsyncTask is
started via the execute() method.


The execute() method
calls the doInBackground() and
the onPostExecute() method.


The doInBackground() method
contains the coding instruction which should be performed in a
background thread. This method runs automatically in a separate Thread.


The onPostExecute() method
synchronize itself again with the user interface thread and allows to
update it. This method is called by the framework once the doInBackground() method
finishes.


To use AsyncTask you
must subclass it. AsyncTask uses generics and varargs. The parameters
are the following AsyncTask <TypeOfVarArgParams , ProgressValue ,
ResultValue> .


TypeOfVarArgParams is
passed into the doInBackground() method
as input, ProgressValue is used for progress information and ResultValue
must be returned from doInBackground() method
and is passed to onPostExecute() as
parameter.









3.4. FEEDBACK VIA PROGRESSBAR





For providing
feedback to the user you can use the ProgressBar dialog,
which allow to display progress to the user. The Javadoc of ProgressBar gives
a nice example of its usage.


Alternatively you can
provide progress feedback in the activities title bar.









3.5. CONCURRENCY AND LIFECYLE





One challenge in
using threads is to consider the lifecycle of the application. The
Android system may kill your activity or trigger a configuration change
which also will restart your activity.


You also need to
handle open dialogs, as dialogs are always connected to the activity
which created them. In case the activity gets restarted and you access
an existing dialog you receive an "View not attached to window manager"
exception.


To save an object
your can use the method onRetainNonConfigurationInstance() method.
This method allows to save one object if the activity will be soon
restarted.


To retrieve this
object you can use the getLastNonConfigurationInstance() method.
This way can you can save an object, e.g. a running thread, even if the
activity is restarted.




getLastNonConfigurationInstance()
returns
null if the Activity is
started the first time or if it has been finished via the finish()
method.


If more then one
object should be saved then you can implement the class "Application".
This class can be used to access object which should be cross activities
or available for the whole application lifecycle. In the onCreate() and
onTerminate() you can create / destroy the object and make them
available via public fields or getters. To use your application class
assign the classname to the android:name attribute of your application.



<application android:icon="@drawable/icon" android:label="@string/app_name"
android:name="MyApplicationClass">
<activity android:name=".ThreadsLifecycleActivity"
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>


You can acess the
Application via the getApplication() method
in your activity.










4. TUTORIAL: HANDLER





In this example we use
the Handler class
to update a ProgressBar view
in a background Thread.


Create a new Android
project called "de.vogella.android.handler" with the Activity"ProgressTestActivity".
Create the following layout main.xml.
This layout contains the ProgressBar and sets its appearance via a style.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

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

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startProgress"
android:text="Start Progress" >
</Button>

</LinearLayout>


Change your Activity to
the following:



package de.vogella.android.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ProgressBar;

public class ProgressTestActivity extends Activity {
private Handler handler;
private ProgressBar progress;



/** Called when the activity is first created. */

@Override

public


void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState); setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1); handler =

new
Handler(); }

public


void
startProgress(View view) {
// Do something long
Runnable runnable =

new
Runnable() {
@Override


public


void
run() {

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

final


int
value = i;

try
{ Thread.sleep(2000); }

catch
(InterruptedException e) { e.printStackTrace(); } handler.post(new
Runnable() { @Override

public


void
run() { progress.setProgress(value); } }); } } };

new
Thread(runnable).start(); } }


Run your application.
Once you press your button the ProgressBar will get updated from the
background thread.









5. TUTORIAL: ASYNCTASK





In this example we will
use an instance of the AsyncTask class
to download the content of a webpage. We use Android
HttpClient
for this. Create a
new Android project called de.vogella.android.asynctaskwith
an Activity called ReadWebpageAsyncTask.
Add the android.permission.INTERNETpermission
to your <filenname>AndroidManifest.xml</filenname> file.
.


Create the following
layout.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<Button
android:id="@+id/readWebpage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="readWebpage"
android:text="Load Webpage" >
</Button>

<TextView
android:id="@+id/TextView01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Example Text" >
</TextView>

</LinearLayout>


Change your activity to
the following:



package de.vogella.android.asynctask;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class ReadWebpageAsyncTask extends Activity {
private TextView textView;



/** Called when the activity is first created. */

@Override

public


void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState); setContentView(R.layout.main);
textView = (TextView) findViewById(R.id.TextView01); }

private


class
DownloadWebPageTask

extends
AsyncTask<String, Void, String> {
@Override


protected
String doInBackground(String... urls) { String response =
"";

for
(String url : urls) { DefaultHttpClient client =

new
DefaultHttpClient(); HttpGet httpGet =

new
HttpGet(url);

try
{ HttpResponse execute = client.execute(httpGet); InputStream
content = execute.getEntity().getContent(); BufferedReader buffer =

new
BufferedReader(new
InputStreamReader(content)); String s =
"";

while
((s = buffer.readLine()) != null) { response += s; } }

catch
(Exception e) { e.printStackTrace(); } }

return
response; }
@Override


protected


void
onPostExecute(String result) { textView.setText(result); } }

public


void
readWebpage(View view) { DownloadWebPageTask task =

new
DownloadWebPageTask(); task.execute(new
String[] {
"http://www.vogella.com"
}); } }


If you run your
application and press your button then the content of the defined webpage
should be read in the background. Once this process is done your TextView will
be updated.









6. TUTORIAL: ACTIVITY LIFECYCLE AND THREAD





The following example
will download an image from the Internet in a thread and displays a dialog
until the download is done. We will make sure that the thread is preserved
even if the activity is restarted and that the dialog is correctly displayed
and closed.


For this example create
the Android project "de.vogella.android.threadslifecycle" and the Activity
"ThreadsLifecycleActivity". Also add the permission to use the Internet to
your app. Details for this can found here: Networking
with Android
.


You should have the
following AndroidManifest.xml file.



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.vogella.android.threadslifecycle"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />

<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>

<application
android:icon="@drawable/icon"
android:label="@string/app_name" >
<activity
android:name=".ThreadsLifecycleActivity"
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>


Change the layout main.xml to
the following.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="downloadPicture"
android:text="Click to start download" >
</Button>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="resetPicture"
android:text="Reset Picture" >
</Button>
</LinearLayout>

<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/icon" >
</ImageView>

</LinearLayout>


Now adjust your activity.
In this activity the thread is saved and the dialog is closed if the
activity is destroyed.



package de.vogella.android.threadslifecycle;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
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.view.View;
import android.widget.ImageView;

public class ThreadsLifecycleActivity extends Activity {
// Static so that the thread access the latest attribute
private static ProgressDialog dialog;
private static ImageView imageView;
private static Bitmap downloadBitmap;
private static Handler handler;
private Thread downloadThread;



/** Called when the activity is first created. */

@Override

public


void
onCreate(Bundle savedInstanceState) {

super
.onCreate(savedInstanceState); setContentView(R.layout.main);
// Create a handler
to update the UI
handler =

new
Handler();
// get the latest imageView after restart of the application
imageView
= (ImageView) findViewById(R.id.imageView1);
// Did we already
download the image?


if
(downloadBitmap != null) {
imageView.setImageBitmap(downloadBitmap); }
// Check if the
thread is already running
downloadThread = (Thread)
getLastNonConfigurationInstance();

if
(downloadThread != null && downloadThread.isAlive()) { dialog =
ProgressDialog.show(this,
"Download",
"downloading"); } }

public


void
resetPicture(View view) {

if
(downloadBitmap != null) { downloadBitmap = null; }
imageView.setImageResource(R.drawable.icon); }

public


void
downloadPicture(View view) { dialog = ProgressDialog.show(this,
"Download",
"downloading");
downloadThread =

new
MyThread(); downloadThread.start(); }
// Save the thread
@Override

public
Object onRetainNonConfigurationInstance() {

return
downloadThread; }
// dismiss dialog if
activity is destroyed

@Override


protected


void
onDestroy() {

if
(dialog != null && dialog.isShowing()) { dialog.dismiss(); dialog
= null; }

super
.onDestroy(); }
// Utiliy method to
download image from the internet


static


private
Bitmap downloadBitmap(String url)

throws
IOException { HttpUriRequest request =

new
HttpGet(url.toString()); HttpClient httpClient =

new
DefaultHttpClient(); HttpResponse response =
httpClient.execute(request); StatusLine statusLine =
response.getStatusLine();

int
statusCode = statusLine.getStatusCode();

if
(statusCode == 200) { HttpEntity
entity = response.getEntity();

byte
[] bytes = EntityUtils.toByteArray(entity); Bitmap bitmap =
BitmapFactory.decodeByteArray(bytes, 0,
bytes.length);

return
bitmap; }

else
{

throw


new
IOException("Download
failed, HTTP response code "
+ statusCode +
" - " +
statusLine.getReasonPhrase()); } }

static


public


class
MyThread

extends
Thread {
@Override


public


void
run() {

try
{ //
Simulate a slow network


try
{

new
Thread().sleep(5000); }

catch
(InterruptedException e) { e.printStackTrace(); }
downloadBitmap = downloadBitmap("http://www.vogella.com/img/lars/LarsVogelArticle7.png");
handler.post(new
MyRunnable()); }

catch
(IOException e) { e.printStackTrace(); }

finally
{ } } }

static


public


class
MyRunnable

implements
Runnable {

public


void
run() { imageView.setImageBitmap(downloadBitmap);
dialog.dismiss(); } } }


Run your application and
press the button to start a download. You can test the correct lifecycle
behavior in the emulator via pressing "Ctrl+F11" as this changes the
orientation.


It is important to note
that the Thread is a static inner class. It is important to use a static
inner class for your background process because otherwise the inner class
will contain a reference to the class in which is was created. As the thread
is passed to the new instance of your activity this would create a memory
leak as the old activity would still be referred to by the Thread.









7. STRICTMODE





StrictMode is available
as of API 9, therefore make sure to use Android 2.3.3. As discussed you
should avoid performing long running operations on the UI thread. This
includes file and network access. It is sometimes difficult to remember to
make all the right things in your application during development. That is
were StrictMode comes in. It allows to setup policies in your application to
avoid doing incorrect things. For example the following setup will crash
your application if it violates some of the Android policies. StrictMode
should only be used during development and not in your live application.


Create the project called "de.vogella.android.strictmode" with the Activity called TestStrictMode.
The following will set strict rules for your application. As the activity
violates these settings you application will crash.



package de.vogella.android.strictmode;

import java.io.BufferedWriter;
import java.io.OutputStreamWriter;

import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;

public class TestStrictMode extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll().penaltyLog().penaltyDeath().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
.penaltyLog().penaltyDeath().build());

super.onCreate(savedInstanceState);
setContentView(R.layout.main);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String eol = System.getProperty("line.separator");
try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(openFileOutput("myfile", MODE_WORLD_WRITEABLE)));
writer.write("This is a test1." + eol);
writer.write("This is a test2." + eol);
writer.write("This is a test3." + eol);
writer.write("This is a test4." + eol);
writer.write("This is a test5." + eol);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}









8. TRACEVIEW AND MEMORY DUMP







Traceview
is a graphical
viewer to see logs created by an Android application. Via Traceview you can
find errors in your application and measure its performance.








8.1. USING TRACEVIEW IN ECLIPSE





Eclipse supports
tracing directly in the DDMS perspective. In the devices view select
your application process and select "Start Method Profiling".






Use your application
and re-press the same button to stop profiling. This will open a new
editor which shows you the result of traceview.










You can zoom into the
graphic to get more details.









8.2. USING TRACEVIEW FROM THE COMMAND LINE





To start tracing some
code put the following code snippet around it.



android.os.Debug.startMethodTracing("yourstring");

// ... your code is here

android.os.Debug.stopMethodTracing();


The parameter
"yourstring" tells the system that it should store the data under
"/sdcard/yourstring.trace". To save data on the sdcard your application
needs the WRITE_EXTERNAL_STORAGE permission. After running your
application you can use Traceview via adb .



adb pull /sdcard/yourstring.trace
traceview yourstring


This will start
Traceview which allow you to analyse your performance data via a
graphical way. The DDMS view has also a trace button available. This
will trace the running application and does not require an additional
authorization.









8.3. MEMORY DUMPS





You can create a
memory snapshot and analyse it with the Eclipse
Memory Analyzer
.










9. THANK YOU







Please help me to support
this article:









10. QUESTIONS AND DISCUSSION





Before posting questions,
please see the vogella
FAQ
. If you have questions or find an error in this article please use
the www.vogella.com
Google Group
. I have created a short list how
to create good questions
which
might also help you.









11. LINKS AND LITERATURE











11.1. SOURCE CODE







Source Code of Examples









11.2. CONCURRENCY RESOURCES







Java Concurrency










11.4. VOGELLA RESOURCES







vogella Training
Android
and Eclipse Training from the vogella team




Android Tutorial
Introduction
to Android Programming




GWT Tutorial
Program in
Java and compile to JavaScript and HTML




Eclipse RCP Tutorial
Create
native applications in Java




JUnit Tutorial
Test your
application




Git Tutorial
Put
everything you have under distributed version control system







مواضيع متعلقة


اهم الفاعليات لهذا الشهر


الشبكات الإجتماعية

تغريدات تويتر