Android Applications Tutorial
22. Threads and AsyncTask

  • 22.0 Threads
  • 22.1 Threads Using Handler - Example 1
  • 22.2 Threads Using Handler - Example 2
  • 22.3 AsyncTask




22.0 Threads

We want our Android application to be downright snappy. End users are used to responsive apps on the mobile and any small delay is perceived as un-responsiveness or worse they may think the application has hung. Responding to user input quickly typically within in 200 milliseconds should be our goal. At least, we need to make sure we respond within less than5 seconds. If a main thread is blocked for more than 5 seconds the user is presented with the infamousapplication not responding(ANR) dialog.

So, what is a thread?
Whenever we start an Android application, a thread calledmainis automatically created. Themain thread, also called theUI thread, is very important because it is in charge of dispatching the events to the appropriate widgets and this includes the drawing events. It is also the thread we interact with Android widgets on. For instance, if you touch the a button on screen, the UI thread dispatches the touch event to the widget which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget to redraw itself.

If you want to see how bad this can look, write a simple application with a button that invokes Thread.sleep(5000) in its OnClickListener . The button will remain in its pressed state for about 5 seconds

The main Java code looks like this:

package com.bogotobogo.singlethread;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class SingleThread extends Activity {/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button start = (Button) findViewById(R.id.Button01);start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});}}


When this happens, it is very easy for the user toperceivethe application as slow.

One of the basic principles to provide a very responsive application is to handle any time consuming code in a separate thread so that we can avoid lengthy operations on the UI thread. We must use extra threads (backgroundorworkerthreads) to perform these operations.

Android provides a few means to set up background threads, yet allow them to safely interact with the UI on the UI thread.

Thesafely interactis crucial. We cannot modify any part of the UI from a background thread. It must be done on the UI thread. This generally means that there will need to be some coordination between background threadsdoingthe work and the UI threadshowingthe results of that work.

Let's take the example of a click listener downloading an image over the network and displaying it in an ImageView:

public void onClick(View v) {  new Thread(new Runnable() {    public void run() {      Bitmap b = loadImageFromNetwork();      mImageView.setImageBitmap(b);    }  }).start();}

At first, this code seems to be a good solution to your problem, as it does not block the UI thread. Unfortunately, it violates the single thread model: the Android UI toolkit is notthread-safeand must always be manipulated on the UI thread. In this piece of code, the ImageView is manipulated on a worker thread, which can cause really weird problems. Tracking down and fixing such bugs can be difficult and time-consuming.

Android offers several ways to access the UI thread from other threads. You may already be familiar with some of them but here is a comprehensive list:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)
  • Handler

The most flexible ways of making an Android-friendly background thread is to create an instance of aHandlersubclass. We need only one object per activity, and we do not need to manually register it. Just creating the instance is sufficient to register it with the Android threading subsystem.

So, it is very essential to understand about how to create new threads (worker or background threads) and how to come back to the parent thread.



22.1 Threads Using Handler - Example 1

How do the two threads, parent/UI and the worker threads, communicate? Via theHandler.

A Handler allows us to send and processMessageandRunnableobjects associated with a thread'sMessageQueue. Each Handler instance is associated with a single thread and that thread's message queue.

So, let us take the handler from themainthread and see how we can use it to communicate with achildthread.

When a handler is created, it is associated by default with the current thread. So, we have this piece of code in the main activity class:

public class HandlerThread extends Activity{   private ProgressDialog progressDialog;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        ((Button) findViewById(R.id.Button01))        .setOnClickListener(new OnClickListener() {        @Overridepublic void onClick(View view) {DoSomething();}              });}private Handler messageHandler = new Handler() {public void handleMessage(Message msg) {super.handleMessage(msg);progressDialog.dismiss();}};}

Now, on click of the button, the DoSomething() method is invoked. Assuming that this is a very time consuming task, we're creating a thread and do the task and return from that thread as shown in the code below:

protected void DoSomething() {// TODO Auto-generated method stubprogressDialog = ProgressDialog.show(this, "", "Doing something...");new Thread() {public void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {}messageHandler.sendEmptyMessage(0);}}.start();}

Since it is time consuming, we're starting aProgressDialogjust to inform the end user that some activity is happening. Then, we start the thread, make it sleep for 5000 milliseconds and send back an empty message through the message handler. ThemessageHandler.sendEmptyMessage(0)is the callback on the parent thread�s messageHandler to inform that the child thread has finished its work. In this example, we're sending an empty message. But this can be the means of communication and exchange of data from the child thread to the parent Thread.

Now the control returns tohandleMessage()call back method. That method shown in the beginning justdismissesthe progressDialog.




Files used in this thread via handler example,HandlerThread.zip


22.2 Threads Using Handler - Example 2

Here is another similar example with a Java code:

package com.bogotobogo.threadb;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.widget.ProgressBar;public class HandlerThreadB extends Activity {ProgressBar bar;@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);setContentView(R.layout.main);bar=(ProgressBar)findViewById(R.id.progress);}public void onStart() {super.onStart();bar.setProgress(0);new Thread(new Runnable() {public void run() {try {for (int i=0;i<50;i++) {Thread.sleep(1000);handler.sendEmptyMessage(0);}}catch (Throwable t) {}}}).start();}Handler handler=new Handler() {@Overridepublic void handleMessage(Message msg) {bar.incrementProgressBy(2);}};}

We create an instance ofHandlerand it has implementationhandleMessage(). Actually for any message received, we update theProgressBarby 2 poins, and then exit the message handler.

InonStart(), we set up a background thread. This thread does something.




Files used in this thread via handler example B,HandlerThreadB.zip



22.3 AsyncTask

AsyncTaskenables proper and easy use of theUI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers. In other words, Android will handle all of the chores of doing work on the UI thread not on a background thread. Also, Android itself allocates and removes that background thread.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread.

An asynchronous task is defined by 3 generic types, calledParams,ProgressandResult, and 4 steps, calledbegin,doInBackground,processProgressandend.


AsyncTask's generic types

The detail information about the three types used by an asynchronous task are the following:

  • Paramsthe type of the parameters sent to the task upon execution.
  • Progressthe type of the progress units published during the background computation.
  • Resultthe type of the result of the background computation.

Not all types are always used by an asynchronous task. To mark a type as unused, simply use the typeVoid:

 private class MyTask extends AsyncTask { ... }


The 4 steps

When an asynchronous task is executed, the task goes through 4 steps:

  • onPreExecute(), invoked on the UI thread immediately after the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
  • doInBackground(Params...), , invoked on the background thread immediately afteronPreExecute(), finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also usepublishProgress(Progress...), to publish one or more units of progress. These values are published on the UI thread, in theonProgressUpdate(Progress...), step.
  • onProgressUpdate(Progress...), , invoked on the UI thread after a call topublishProgress(Progress...), . The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
  • onPostExecute(Result), , invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.


Threading Rules

There are a few threading rules that must be followed for this class to work properly:

  • The task instance must be created on the UI thread.
  • Do not callonPreExecute(),onPostExecute(Result),doInBackground(Params...),onProgressUpdate(Progress...)manually.
  • The task can be executed only once (an exception will be thrown if a second execution is attempted.)

AsyncTask Example

This example is an implementation of aListActivitythat uses anAsyncTask.

In this example, rather than simply pass the list of Java book to anArrayAdapter, we simulate needing to work to create these list in the background usingAddStringTask. OurAsyncTask, theAddStringTaska sublass ofAsyncTask, will override at least one methoddoInBackground(Params...), and most often will override a second oneonPostExecute(Result).


Let's look at our code,AsyncThrea.java:

package com.bogotobogo.threadc;import android.app.ListActivity;import android.os.AsyncTask;import android.os.Bundle;import android.os.SystemClock;import android.widget.ArrayAdapter;import android.widget.Toast;import java.util.ArrayList;public class AsyncThread extends ListActivity {private static String[] items={"Hello, Android: Introducing Google's Mobile Development Platform ", "Professional Android 2 Application Development ", "Unlocking Android: A Developer's Guide","Android Application Development: Programming with the Google SDK", "Pro Android 2", "Beginning Android 2","Android Programming Tutorials, 2nd Edition", "Android Wireless Application Development", "Pro Android Games","Beginning Smartphone Web Development", "The Busy Coder's Guide to Advanced Android Development", "Head First Java, 2nd Edition","Effective Java (2nd Edition)","Sams Teach Yourself Java in 24 Hours (5th Edition)","Core Java(TM), Volume I--Fundamentals (8th Edition)","Java In A Nutshell, 5th Edition","Thinking in Java (4th Edition)","Java Concurrency in Practice","SCJP Sun Certified Programmer for Java 6 Exam 310-065","Java How to Program, 7th Edition","Learning Java","Head First Design Patterns","Beginning Java� EE 6 Platform with GlassFish� 3","Head First Servlets and JSP","Java Message Service","Core Java(TM), Volume I--Fundamentals (8th Edition)","Beginning Programming with Java For Dummies"};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);setListAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_1,new ArrayList()));new AddStringTask().execute();}class AddStringTask extends AsyncTask {@Overrideprotected Void doInBackground(Void... unused) {for (String item : items) {publishProgress(item);SystemClock.sleep(200);}return(null);}@Overrideprotected void onProgressUpdate(String... item) {((ArrayAdapter)getListAdapter()).add(item[0]);}@Overrideprotected void onPostExecute(Void unused) {Toast.makeText(AsyncThread.this, "Done - Finished updating Java Book List!",Toast.LENGTH_SHORT).show();}}}

Let's look at theAddStringTask:

class AddStringTask extends AsyncTask {

We want to pass each string generated by our background task toonProgressUpdate()because we want to add it to our list. So, our second type should beStringwhile others are void types.

ThedoInBackground() method:

@Overrideprotected Void doInBackground(Void... unused) {for (String item : items) {publishProgress(item);SystemClock.sleep(200);}return(null);}

This method is invoked in a background thread. Here, we iterate over our list of books, the callpublishProgress(item)for each item. Then, make it sleep 1/4 second to simulate real work being done.

onProgressUpdate()method:

@Overrideprotected void onProgressUpdate(String... item) {((ArrayAdapter)getListAdapter()).add(item[0]);}

This method is called on the UI thread, and we want to do something to let the user know we are making progress on loading these strings. In this case, we simply add the string to theArrayAdapter, so it is appended to the end of the list.

onProgressUpdate(String... )receives aString...varargs because that is the second data type in our class declaration. Since we are passing only one string per call toonProgressUpdate(), we need to examine just first entry in the varargs array.

TheonPostExecute()method is called on the UI thread, and we want to do something to indicate that the background work is complete. Here we simply raise aToast.

The last thing we look at is :

new AddStringTask().execute();

To useAddStringTask(), we just create an instance and callexecute()on it. That starts the chain of events eventually leading to the background thread doing its work.

Time to run our application.





Files used in this thread via handler example B,ThreadAsync.zip

更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. android 混淆jar及apk的心得
  2. android 中的 handler
  3. android 图片叠加效果
  4. android source
  5. 【Android(安卓)Demo】让Android支持自定
  6. Android字体
  7. Android(安卓)Fresco - SimpleDraweeView
  8. Android(安卓)开发中的一些小技巧
  9. Android工具包AndroidUtils
  10. Android中ListView的使用