Explain the use of AsyncTask in Android

Keywords: Android Java Apache xml

Reprinted from http://blog.csdn.net/liuhe688/article/details/6532519

stay Android There are two ways to implement asynchronous task mechanism, Handler and AsyncTask.

Handler mode needs to create a new thread for each task. After the task is completed, it sends a message to the UI thread through the Handler instance to complete the update of the interface. This method has fine control over the whole process, but it also has shortcomings. For example, the code is relatively bulky, and it is not easy to control the thread accurately when multiple tasks are executed at the same time. Knowledge about Handler It's also introduced in the previous section. If you don't know it, you can refer to it.

To simplify the operation, Android 1.5 provides the tool class android.os.AsyncTask, which makes it easier to create asynchronous tasks without writing task threads and Handler instances.

Let's first look at the definition of AsyncTask:

  1. public abstract class AsyncTask<Params, Progress, Result> {  

The three generic types represent "input parameters for starting task execution", "progress of background task execution" and "types of background calculation results". In certain situations, not all types are used, if not, they can be used. Java The. lang.Void type is replaced.

The execution of an asynchronous task generally consists of the following steps:

1.execute(Params... params), to perform an asynchronous task, we need to call this method in the code, trigger the execution of the asynchronous task.

2.onPreExecute(), which is executed immediately after execute(Params... params) is called, is generally used to mark the UI before performing background tasks.

3.doInBackground(Params... params), which is executed immediately after onPreExecute() is completed, is used to perform more time-consuming operations. This method receives input parameters and returns the calculation results. PushProgress (Progress... values) can be called during execution to update progress information.

4.onProgressUpdate(Progress... values), which is executed when publishProgress(Progress... values) is called, updates progress information directly to UI components.

5.onPostExecute(Result result), when the background operation is finished, this method will be called. The result of calculation will be passed as a parameter to this method, and the result will be displayed directly on the UI component.

When using it, there are several points that need special attention:

1. Instances of asynchronous tasks must be created in UI threads.

2. The execute (Params... params) method must be invoked in the UI thread.

3. Do not manually call onPreExecute(), doInBackground(Params... params), onProgressUpdate(Progress... values), onPostExecute(Result result).

4. The information of UI components cannot be changed in doInBackground(Params... params).

5. A task instance can only be executed once, and an exception will be thrown if executed the second time.

Next, let's look at how to use AsyncTask to perform asynchronous task operations. First, we build a project with the following structure:

The structure is relatively simple. Let's first look at MainActivity.java's code:

  1. package com.scott.async;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.InputStream;  
  5.   
  6. import org.apache.http.HttpEntity;  
  7. import org.apache.http.HttpResponse;  
  8. import org.apache.http.HttpStatus;  
  9. import org.apache.http.client.HttpClient;  
  10. import org.apache.http.client.methods.HttpGet;  
  11. import org.apache.http.impl.client.DefaultHttpClient;  
  12.   
  13. import android.app.Activity;  
  14. import android.os.AsyncTask;  
  15. import android.os.Bundle;  
  16. import android.util.Log;  
  17. import android.view.View;  
  18. import android.widget.Button;  
  19. import android.widget.ProgressBar;  
  20. import android.widget.TextView;  
  21.   
  22. public class MainActivity extends Activity {  
  23.   
  24.     private static final String TAG = "ASYNC_TASK";  
  25.   
  26.     private Button execute;  
  27.     private Button cancel;  
  28.     private ProgressBar progressBar;  
  29.     private TextView textView;  
  30.   
  31.     private MyTask mTask;  
  32.   
  33.     @Override  
  34.     public void onCreate(Bundle savedInstanceState) {  
  35.         super.onCreate(savedInstanceState);  
  36.         setContentView(R.layout.main);  
  37.   
  38.         execute = (Button) findViewById(R.id.execute);  
  39.         execute.setOnClickListener(new View.OnClickListener() {  
  40.             @Override  
  41.             public void onClick(View v) {  
  42.                 //Note that you need a new instance at a time, and the new task can only be executed once, otherwise there will be exceptions.  
  43.                 mTask = new MyTask();  
  44.                 mTask.execute("http://www.baidu.com");  
  45.   
  46.                 execute.setEnabled(false);  
  47.                 cancel.setEnabled(true);  
  48.             }  
  49.         });  
  50.         cancel = (Button) findViewById(R.id.cancel);  
  51.         cancel.setOnClickListener(new View.OnClickListener() {  
  52.             @Override  
  53.             public void onClick(View v) {  
  54.                 //Cancel an ongoing task and the onCancelled method will be called  
  55.                 mTask.cancel(true);  
  56.             }  
  57.         });  
  58.         progressBar = (ProgressBar) findViewById(R.id.progress_bar);  
  59.         textView = (TextView) findViewById(R.id.text_view);  
  60.   
  61.     }  
  62.   
  63.     private class MyTask extends AsyncTask<String, Integer, String> {  
  64.         //The onPreExecute method is used to do some UI operations before performing background tasks  
  65.         @Override  
  66.         protected void onPreExecute() {  
  67.             Log.i(TAG, "onPreExecute() called");  
  68.             textView.setText("loading...");  
  69.         }  
  70.   
  71.         //The doInBackground method performs background tasks internally and cannot modify the UI in this method  
  72.         @Override  
  73.         protected String doInBackground(String... params) {  
  74.             Log.i(TAG, "doInBackground(Params... params) called");  
  75.             try {  
  76.                 HttpClient client = new DefaultHttpClient();  
  77.                 HttpGet get = new HttpGet(params[0]);  
  78.                 HttpResponse response = client.execute(get);  
  79.                 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
  80.                     HttpEntity entity = response.getEntity();  
  81.                     InputStream is = entity.getContent();  
  82.                     long total = entity.getContentLength();  
  83.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  84.                     byte[] buf = new byte[1024];  
  85.                     int count = 0;  
  86.                     int length = -1;  
  87.                     while ((length = is.read(buf)) != -1) {  
  88.                         baos.write(buf, 0, length);  
  89.                         count += length;  
  90.                         //Call publishProgress to announce progress, and finally the onProgressUpdate method will be executed  
  91.                         publishProgress((int) ((count / (float) total) * 100));  
  92.                         //To demonstrate progress, sleep 500 milliseconds  
  93.                         Thread.sleep(500);  
  94.                     }  
  95.                     return new String(baos.toByteArray(), "gb2312");  
  96.                 }  
  97.             } catch (Exception e) {  
  98.                 Log.e(TAG, e.getMessage());  
  99.             }  
  100.             return null;  
  101.         }  
  102.   
  103.         //onProgressUpdate Method for Updating Progress Information  
  104.         @Override  
  105.         protected void onProgressUpdate(Integer... progresses) {  
  106.             Log.i(TAG, "onProgressUpdate(Progress... progresses) called");  
  107.             progressBar.setProgress(progresses[0]);  
  108.             textView.setText("loading..." + progresses[0] + "%");  
  109.         }  
  110.   
  111.         //The onPostExecute method is used to update the UI and display the results after performing background tasks.  
  112.         @Override  
  113.         protected void onPostExecute(String result) {  
  114.             Log.i(TAG, "onPostExecute(Result result) called");  
  115.             textView.setText(result);  
  116.   
  117.             execute.setEnabled(true);  
  118.             cancel.setEnabled(false);  
  119.         }  
  120.   
  121.         //The onCancelled method is used to change the UI when tasks are cancelled  
  122.         @Override  
  123.         protected void onCancelled() {  
  124.             Log.i(TAG, "onCancelled() called");  
  125.             textView.setText("cancelled");  
  126.             progressBar.setProgress(0);  
  127.   
  128.             execute.setEnabled(true);  
  129.             cancel.setEnabled(false);  
  130.         }  
  131.     }  
  132. }  

The layout file main.xml code is as follows:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent">  
  6.     <Button  
  7.         android:id="@+id/execute"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="execute"/>  
  11.     <Button  
  12.         android:id="@+id/cancel"  
  13.         android:layout_width="fill_parent"  
  14.         android:layout_height="wrap_content"  
  15.         android:enabled="false"  
  16.         android:text="cancel"/>  
  17.     <ProgressBar   
  18.         android:id="@+id/progress_bar"   
  19.         android:layout_width="fill_parent"   
  20.         android:layout_height="wrap_content"   
  21.         android:progress="0"  
  22.         android:max="100"  
  23.         style="?android:attr/progressBarStyleHorizontal"/>  
  24.     <ScrollView  
  25.         android:layout_width="fill_parent"   
  26.         android:layout_height="wrap_content">  
  27.         <TextView  
  28.             android:id="@+id/text_view"  
  29.             android:layout_width="fill_parent"   
  30.             android:layout_height="wrap_content"/>  
  31.     </ScrollView>  
  32. </LinearLayout>  

Because we need to access the network, we also need to add permission to access the network in AndroidManifest.xml:

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

Let's look at the runtime interface:

These screenshots are the initial interface, the interface when performing asynchronous tasks, the interface after successful execution, and the interface after canceling tasks. After successful execution, the whole process log is printed as follows:

If we press the cancel button while performing the task, the log will be printed as follows:

You can see that the onCancelled() method will be called, and the onPostExecute(Result result) method will no longer be called.

The basic application of AsyncTask is introduced above. Some friends may wonder how AsyncTask is executed internally. What is the difference between the execution process of AsyncTask and the use of Handler? The answer is: AsyncTask is a good encapsulation of Thread+Handler, which can still be seen in android.os.AsyncTask code. Here's a detailed introduction to the implementation principle of AsyncTask.

Let's first look at the outline view of AsyncTask:

We can see that there are several key steps in the method, doInBackground(Params... params) is an abstract method, we must override this method when we inherit AsyncTask; onPreExecute(), onProgressUpdate(Progress... values), onPostExecute(Result result), onCancelled() are empty, we can selectively override them when we need them. PushProgress (Progress... Values) is final ized and cannot be overridden. It can only be invoked. We usually use doInBackground(Params... This method is called in params; in addition, we can see that there is an enumeration class for Status and a getStatus() method. The code snippet for the enumeration class for Status is as follows:

  1. //Initial state  
  2. private volatile Status mStatus = Status.PENDING;  
  3.   
  4. public enum Status {  
  5.     /** 
  6.      * Indicates that the task has not been executed yet. 
  7.      */  
  8.     PENDING,  
  9.     /** 
  10.      * Indicates that the task is running. 
  11.      */  
  12.     RUNNING,  
  13.     /** 
  14.      * Indicates that {@link AsyncTask#onPostExecute} has finished. 
  15.      */  
  16.     FINISHED,  
  17. }  
  18.   
  19. /** 
  20.  * Returns the current status of this task. 
  21.  * 
  22.  * @return The current status. 
  23.  */  
  24. public final Status getStatus() {  
  25.     return mStatus;  
  26. }  
As you can see, the initial state of AsyncTask is PENDING, which represents the undetermined state, RUNNING represents the execution state, FINISHED represents the end state. These states are used in many places in the life cycle of AsyncTask, and they are very important.

After introducing the related content of outline view, we will focus on the execution process of AsyncTask from execute(Params... params). Let's take a look at the code segment of execute(Params... params):

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  2.     if (mStatus != Status.PENDING) {  
  3.         switch (mStatus) {  
  4.             case RUNNING:  
  5.                 //Throw an exception if the task is being performed  
  6.                 //It is worth mentioning that after cancel is called to cancel the task, the status is still not RUNNING  
  7.                 throw new IllegalStateException("Cannot execute task:"  
  8.                         + " the task is already running.");  
  9.             case FINISHED:  
  10.                 //Throw an exception if the task has been completed  
  11.                 throw new IllegalStateException("Cannot execute task:"  
  12.                         + " the task has already been executed "  
  13.                         + "(a task can be executed only once)");  
  14.         }  
  15.     }  
  16.   
  17.     //Change status to RUNNING  
  18.     mStatus = Status.RUNNING;  
  19.   
  20.     //Call the onPreExecute method  
  21.     onPreExecute();  
  22.   
  23.     mWorker.mParams = params;  
  24.     sExecutor.execute(mFuture);  
  25.   
  26.     return this;  
  27. }  
There are three unfamiliar variables involved in the code: mWorker, sExecutor, mFuture. We will also take a look at their Lushan reality:

For sExecutor, it is an example of java.util.concurrent.ThreadPoolExecutor, which manages thread execution. The code is as follows:

  1. private static final int CORE_POOL_SIZE = 5;  
  2. private static final int MAXIMUM_POOL_SIZE = 128;  
  3. private static final int KEEP_ALIVE = 10;  
  4.   
  5. //Create a new queue to store threads  
  6. private static final BlockingQueue<Runnable> sWorkQueue =  
  7.         new LinkedBlockingQueue<Runnable>(10);  
  8. //New Thread Factory  
  9. private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
  10.     private final AtomicInteger mCount = new AtomicInteger(1);  
  11.     //Create a new thread  
  12.     public Thread newThread(Runnable r) {  
  13.         return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
  14.     }  
  15. };  
  16. //Create a new thread pool executor to manage thread execution  
  17. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
  18.         MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);  
mWorker is actually an implementation object instance of an abstract inner class of AsyncTask, which implements the call() method in the Callable < Result > interface. The code is as follows:

  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
  2.     Params[] mParams;  
  3. }  
And mFuture is actually an example of java.util.concurrent.FutureTask. Here's the information about its FutureTask class:

  1. /** 
  2.  * A cancellable asynchronous computation. 
  3.  * ... 
  4.  */  
  5. public class FutureTask<V> implements RunnableFuture<V> {  

  1. public interface RunnableFuture<V> extends Runnable, Future<V> {  
  2.     /** 
  3.      * Sets this Future to the result of its computation 
  4.      * unless it has been cancelled. 
  5.      */  
  6.     void run();  
  7. }  
You can see that FutureTask is a class for asynchronous computing that can be cancelled halfway.

The following are examples of mWorker and mFuture in AsyncTask:

  1. private final WorkerRunnable<Params, Result> mWorker;  
  2. private final FutureTask<Result> mFuture;  
  3.   
  4. public AsyncTask() {  
  5.     mWorker = new WorkerRunnable<Params, Result>() {  
  6.         //After the call method is invoked, the priority is set to the background level, and then the doInBackground method of AsyncTask is invoked.  
  7.         public Result call() throws Exception {  
  8.             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  9.             return doInBackground(mParams);  
  10.         }  
  11.     };  
  12.   
  13.     //In the mFuture instance, mWorker will be invoked to do background tasks, and when completed, the done method will be invoked.  
  14.     mFuture = new FutureTask<Result>(mWorker) {  
  15.         @Override  
  16.         protected void done() {  
  17.             Message message;  
  18.             Result result = null;  
  19.   
  20.             try {  
  21.                 result = get();  
  22.             } catch (InterruptedException e) {  
  23.                 android.util.Log.w(LOG_TAG, e);  
  24.             } catch (ExecutionException e) {  
  25.                 throw new RuntimeException("An error occured while executing doInBackground()",  
  26.                         e.getCause());  
  27.             } catch (CancellationException e) {  
  28.                 //Send a message to cancel the task  
  29.                 message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
  30.                         new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
  31.                 message.sendToTarget();  
  32.                 return;  
  33.             } catch (Throwable t) {  
  34.                 throw new RuntimeException("An error occured while executing "  
  35.                         + "doInBackground()", t);  
  36.             }  
  37.   
  38.             //Send a message displaying the result  
  39.             message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  40.                     new AsyncTaskResult<Result>(AsyncTask.this, result));  
  41.             message.sendToTarget();  
  42.         }  
  43.     };  
  44. }  
We see in the code above that in the done() method of the mFuture instance object, if an exception of CancellationException type is caught, a message of "MESSAGE_POST_CANCEL" is sent; if it is successfully executed, a message of "MESSAGE_POST_RESULT" is sent, and the message is associated with an sHandler object. This sHandler instance is actually an instance of the internal class InternalHandler of AsyncTask, which inherits Handler. Let's analyze its code below:

  1. private static final int MESSAGE_POST_RESULT = 0x1//Display result  
  2. private static final int MESSAGE_POST_PROGRESS = 0x2;   //Update progress  
  3. private static final int MESSAGE_POST_CANCEL = 0x3//Cancel tasks  
  4.   
  5. private static final InternalHandler sHandler = new InternalHandler();  
  6.   
  7. private static class InternalHandler extends Handler {  
  8.     @SuppressWarnings({"unchecked""RawUseOfParameterizedType"})  
  9.     @Override  
  10.     public void handleMessage(Message msg) {  
  11.         AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
  12.         switch (msg.what) {  
  13.             case MESSAGE_POST_RESULT:  
  14.                 // There is only one result  
  15.                 //Call the AsyncTask.finish method  
  16.                 result.mTask.finish(result.mData[0]);  
  17.                 break;  
  18.             case MESSAGE_POST_PROGRESS:  
  19.                 //Call the AsyncTask.onProgressUpdate method  
  20.                 result.mTask.onProgressUpdate(result.mData);  
  21.                 break;  
  22.             case MESSAGE_POST_CANCEL:  
  23.                 //Call the AsyncTask.onCancelled method  
  24.                 result.mTask.onCancelled();  
  25.                 break;  
  26.         }  
  27.     }  
  28. }  
We see that when we process a message and encounter "MESSAGE_POST_RESULT", it calls the finish() method in AsyncTask. Let's look at the definition of finish() method:

  1. private void finish(Result result) {  
  2.     if (isCancelled()) result = null;  
  3.     onPostExecute(result);  //Call onPostExecute to display the results  
  4.     mStatus = Status.FINISHED;  //Change status to FINISHED  
  5. }  
The original finish() method is responsible for calling the onPostExecute(Result result) method to display the results and change the status of the task.

In addition, in the done() method of the mFuture object, when constructing a message, the message contains an AsyncTaskResult type object, and then in the handleMessage(Message msg) method of the sHandler instance object, the object attached to the message is obtained in the following way:

  1. AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
What exactly is this Async Task Result and what does it contain? In fact, it is also an internal class of AsyncTask, a class used to wrap execution results. Let's look at its code structure:

  1. @SuppressWarnings({"RawUseOfParameterizedType"})  
  2. private static class AsyncTaskResult<Data> {  
  3.     final AsyncTask mTask;  
  4.     final Data[] mData;  
  5.   
  6.     AsyncTaskResult(AsyncTask task, Data... data) {  
  7.         mTask = task;  
  8.         mData = data;  
  9.     }  
  10. }  
To see that this AsyncTask Result encapsulates an instance of AsyncTask and some kind of data set, let's look at the code when building the message:

  1. //Send a message to cancel the task  
  2. message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
  3.         new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
  4. message.sendToTarget();  
  1. //Send a message displaying the result  
  2. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  3.          new AsyncTaskResult<Result>(AsyncTask.this, result));  
  4. message.sendToTarget();  
How to use this object when processing messages? Let's take a look at it again.

  1. result.mTask.finish(result.mData[0]);  

  1. result.mTask.onProgressUpdate(result.mData);  
Generally speaking, when we call the execute(Params... params) method, the execute method calls the onPreExecute() method, and then the ThreadPoolExecutor instance sExecutor executes a FutureTask task, in which the doInBackground(Params... params) will be invoked if the developer overwrites the IndoBackground (Params... The publishProgress(Progress... values) method is invoked in the params method, and a MESSAGE_POST_PROGRESS message is sent through the sHandler instance sHandler to update the progress. The onProgressUpdate(Progress... values) method is invoked when sHandler processes the message; if an exception is encountered, a MESSAGE_POST_CANCEL message is sent to cancel the task and sHandler processes the message. The onCancelled() method will be invoked; if the execution is successful, a message of MESSAGE_POST_RESULT will be sent, showing the result that onPostExecute(Result) is used when sHandler processes the message. The result) method is called.

After the introduction above, I believe that friends have realized the essence of AsyncTask. It encapsulates Thread+Handler well, reduces the complexity of developers to deal with problems, and improves the efficiency of development. I hope that friends can appreciate it more.

Posted by SparkleD on Wed, 10 Apr 2019 10:12:32 -0700