HandlerThread and IntentService are unique thread classes in Android system, and there is another important asynchronous thread class AsyncTask in Android. In this article we will analyze AsyncTask.
- Analysis of the routine use of AsyncTask and case implementation
- Differences of AsyncTask in different Android versions
- The working principle of AsyncTask
I. Analysis of the routine use of AsyncTask and case implementation
AsyncTask is a lightweight asynchronous task class that performs background tasks in a thread pool and then passes the progress and final results of the execution to the main thread and updates the UI. AsyncTask is an abstract class that provides three generic parameters: Params, Progress, and Result. The class declaration is as follows:
public abstract class AsyncTask<Params, Progress, Result> {
Copy the code
As can be seen from the class declaration, the AsyncTask abstract class does define three generic types, Params, Progress, and Result, which have the following meanings:
- Params: input parameters to start task execution, such as HTTP request URL
- Progress: indicates the percentage of background tasks executed
- Result: Indicates the Result type returned by background tasks
If AsyncTask does not need to pass specific parameters, these three generic parameters can be replaced by Void. We now create a class that inherits AsyncTask as follows:
package com.zejian.handlerlooper; import android.graphics.Bitmap; import android.os.AsyncTask; /** * Created by zejian * Time 16/9/4. * Description: */ public class DownLoadAsyncTask extends AsyncTask<String,Integer,Bitmap> {/** * onPreExecute is an optional overwrite method * Executed in the main thread, before an asynchronous task is executed, this method is called * to do some marking and preparation work on the UI before performing background tasks, such as displaying a progress bar on the interface. */ @Override protected void onPreExecute() { super.onPreExecute(); } @override protected Bitmap doInBackground(String... params) { return null; } /** * onProgressUpdate is a method that can be optionally overridden * in the main thread, when the progress of the background task changes, Of course we must call publishProgress() in the doInBackground method to set the progress change value * @param values */ @override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } /** * onPostExecute is a method that can optionally be overridden * executed in the main thread, which is called after the asynchronous task is completed * is generally used to update the UI or other operations that must be performed in the main thread, passing the bitmap as * @override protected void onPostExecute(bitmap bitmap) { super.onPostExecute(bitmap); } /** * onCancelled is the method that can be optionally cancelled * in the main thread when the asynchronous task is cancelled, */ @override protected void onCancelled() {super.oncancelled (); }}Copy the code
As shown in the code, we create an asynchronous thread class that inherits from AsyncTask. In terms of generic parameters, we pass String (Url), Integer (progress), and Bitmap as return values. Then the abstract method doInBackground() is overwritten, and the methods onPreExecute(), onProgressUpdate(), onPostExecute(), onCancelled() are overwritten. Their main meanings are as follows:
- (1) onPreExecute(), this method is executed in the main thread, will execute(Params… Params is called to perform UI preparatory work, such as displaying a progress bar on the interface.
- (2) the doInBackground (Params… Params), an abstract method that must be implemented. This method is executed in the thread pool and is used to perform asynchronous tasks that will be executed after the onPreExecute method. The parameter is a mutable type that represents the input parameter of the asynchronous task. In this method, publishProgress(Progress… Values) to update the real-time task progress, while the publishProgress method calls the onProgressUpdate method. In addition, the doInBackground method passes the results of the calculation to the onPostExecute method.
- (3) onProgressUpdate (Progress…). In the main thread, this method is executed on publishProgress(Progress… The values) method is invoked to update the UI progress, such as the current progress of the progress bar.
- (4) onPostExecute(Result), executed in the main thread. After the execution of doInBackground, onPostExecute method will be called by the UI thread, and the return value of doInBackground method will be passed to the UI thread as the parameter of this method. And perform some UI-related operations, such as updating the UI view.
- (5) onCancelled(), which will be called when the asynchronous task is cancelled. Note that onPostExecute will not be executed at this point.
The onPreExecute method is executed first, followed by the doInBackground method. In doInBackground, if the publishProgress method is called, The onProgressUpdate method will be executed, and finally after the doInBackground method is executed, the onPostExecute method will be executed. AsyncTask can be started using the execute method, which is declared as follows:
public final AsyncTask<Params, Progress, Result> execute(Params... params)
Copy the code
This method is a final method, and the parameter type is mutable. In fact, the parameters passed here and doInBackground(Params… Params) method, which ultimately returns an instance object of AsyncTask that can be used for other operations, such as terminating a thread. The startup example is as follows:
new DownLoadAsyncTask().execute(url1,url2,url3);
Copy the code
Of course, in addition to the above, we must follow some rules when using AsyncTask to avoid unnecessary trouble.
- (1) AsyncTask instances must be created in the main thread (UI thread), and the execute method must be called in the main thread
- Do not directly call onPreExecute(), onPostExecute(Result), doInBackground(Params…) , onProgressUpdate (Progress…). These are the methods
- (3) Can not be in doInBackground(Params… Update UI in params
- (5) An AsyncTask object can only be executed once, that is, the execute method can only be called once. Otherwise, an exception will be thrown when the AsyncTask object is called multiple times
At this point, the general method description and use of AsyncTask and matters needing attention are all introduced. Now let’s look at a download case, which is to download a large picture and realize the real-time progress of the download. Let’s look at the implementation of AsynTaskActivity. Java:
package com.zejian.handlerlooper; import android.content.Context; import android.os.AsyncTask; import android.os.Environment; import android.os.PowerManager; import android.widget.Toast; import com.zejian.handlerlooper.util.LogUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; /** * Created by zejian * Time 16/9/4. * Description: */ public class DownLoadAsyncTask extends AsyncTask<String, Integer, String> { private PowerManager.WakeLock mWakeLock; private int ValueProgress=100; private Context context; public DownLoadAsyncTask(Context context){ this.context=context; } /** * sync method which download file * @param params * @return */ @Override protected String doInBackground(String... params) { InputStream input = null; OutputStream output = null; HttpURLConnection connection = null; try { URL url = new URL(params[0]); connection = (HttpURLConnection) url.openConnection(); connection.connect(); // expect HTTP 200 OK, so we don't mistakenly save error report // instead of the file if (connection.getResponseCode() ! = HttpURLConnection.HTTP_OK) { return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage(); } // this will be useful to display download percentage // might be -1: server did not report the length int fileLength = connection.getContentLength(); // download the file input = connection.getInputStream(); //create output output = new FileOutputStream(getSDCardDir()); byte data[] = new byte[4096]; long total = 0; int count; while ((count = input.read(data)) ! = -1) { // allow canceling with back button if (isCancelled()) { input.close(); return null; } total += count; // publishing the progress.... if (fileLength > 0) // only if total length is known publishProgress((int) (total * 100 / fileLength)); // Thread.sleep(100); output.write(data, 0, count); } } catch (Exception e) { return e.toString(); } finally { try { if (output ! = null) output.close(); if (input ! = null) input.close(); } catch (IOException ignored) { } if (connection ! = null) connection.disconnect(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); // take CPU lock to prevent CPU from going off if the user // presses the power button during download PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); mWakeLock.acquire(); //Display progressBar // progressBar.setVisibility(View.VISIBLE); } @Override protected void onPostExecute(String values) { super.onPostExecute(values); mWakeLock.release(); if (values ! = null) LogUtils.e("Download error: "+values); else { Toast.makeText(context, "File downloaded", Toast.LENGTH_SHORT).show(); } } /** * set progressBar * @param values */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); // progressBar.setmProgress(values[0]); //update progressBar if(updateUI! =null){ updateUI.UpdateProgressBar(values[0]); } } /** * get SD card path * @return */ public File getSDCardDir(){ If (Environment) MEDIA_MOUNTED) equals (Environment. GetExternalStorageState ())) {/ / create a folder object, Assignment for the external storage directory String dirName = Environment. External.getexternalstoragedirectory () + "/ MyDownload/"; File f = new File(dirName); if(! f.exists()){ f.mkdir(); } File downloadFile=new File(f,"new.jpg"); return downloadFile; } else{ LogUtils.e("NO SD Card!" ); return null; } } public UpdateUI updateUI; public interface UpdateUI{ void UpdateProgressBar(Integer values); } public void setUpdateUIInterface(UpdateUI updateUI){ this.updateUI=updateUI; }}Copy the code
Just to show you a little bit of code, in the onPreExecute method, we can do some preparatory work, like displaying a progress circle, which is normally displayed for the sake of this demonstration, and also locking the CPU to prevent download breaks, whereas in the doInBackground method, Through HttpURLConnection object to download pictures, and then through the int fileLength = connection. GetContentLength (); PublishProgress ((int) (total * 100 / fileLength)); To update the progress, call the onProgressUpdate method to update the progress bar. Finally, release the CPU lock in the onPostExecute method and notify if the download was successful. Then look at the implementation of the Activity: activity_download.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:customView="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.zejian.handlerlooper.util.LoadProgressBarWithNum
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
customView:progress_radius="100dp"
android:layout_centerInParent="true"
customView:progress_strokeWidth="40dp"
customView:progress_text_size="35sp"
customView:progress_text_visibility="visible"
customView:progress_value="0"
/>
<Button
android:id="@+id/downloadBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start download"
android:layout_centerHorizontal="true"
android:layout_below="@id/progressbar"
android:layout_marginTop="40dp"
/>
</RelativeLayout>
Copy the code
AsynTaskActivity.java
package com.zejian.handlerlooper; import android.Manifest; import android.app.Activity; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.view.View; import android.widget.Button; import com.zejian.handlerlooper.util.LoadProgressBarWithNum; import com.zejian.handlerlooper.util.LogUtils; /** * Created by zejian * Time 16/9/4. * Description:AsynTaskActivity */ public class AsynTaskActivity extends Activity implements DownLoadAsyncTask.UpdateUI { private static int WRITE_EXTERNAL_STORAGE_REQUEST_CODE=0x11; Private static String DOWNLOAD_FILE_JPG_URL = "http://img2.3lian.com/2014/f6/173/d/51.jpg"; private LoadProgressBarWithNum progressBar; private Button downloadBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_download); progressBar= (LoadProgressBarWithNum) findViewById(R.id.progressbar); downloadBtn= (Button) findViewById(R.id.downloadBtn); //create DownLoadAsyncTask final DownLoadAsyncTask downLoadAsyncTask= new DownLoadAsyncTask(AsynTaskActivity.this); //set Interface downLoadAsyncTask.setUpdateUIInterface(this); //start download downloadBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //execute downLoadAsyncTask.execute(DOWNLOAD_FILE_JPG_URL); }}); / / android 6.0 permission to apply for the if (ContextCompat checkSelfPermission (this, the Manifest. Permission. WRITE_EXTERNAL_STORAGE)! = PackageManager. PERMISSION_GRANTED) {/ / android 6.0 API must apply to the WRITE_EXTERNAL_STORAGE permission ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); doNext(requestCode,grantResults); } private void doNext(int requestCode, int[] grantResults) { if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission Granted LogUtils.e("Permission Granted"); } else { // Permission Denied LogUtils.e("Permission Denied"); } } } /** * update progressBar * @param values */ @Override public void UpdateProgressBar(Integer values) { progressBar.setmProgress(values);; }}Copy the code
Realized update UI interface in AsynTaskActivity DownLoadAsyncTask. UpdateUI, used to update the main thread of the progress of the progressBar, due to the use of test version is android6.0, involve the external SD card read permission to apply for, So the SD card permissions are handled specially in the code, LoadProgressBarWithNum is a custom progress bar control. Ok ~, finally look at our run result:
Through this case, WE believe that the use of AsyncTask has been quite clear. That’s about it, and then we’ll talk about the differences between AsyncTask and android versions.
Ii. Differences of AsyncTask in different Android versions
Before Android 3.0, AsyncTask processes tasks in parallel in a thread pool by default. After Android 3.0, AsyncTask processes tasks in parallel in a thread pool by default. In order to avoid concurrency errors caused by AsyncTask processing, AsyncTask uses a single thread to execute tasks in serial. However, this does not mean that serial tasks can only be performed after Android 3.0. We can still use the executeOnExecutor method of AsyncTask to execute tasks in parallel. Next, write a case, execute it on Android 2.3.3 and Android 6.0, and print out the log. The code is as follows:
package com.zejian.handlerlooper; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import com.zejian.handlerlooper.util.LogUtils; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by zejian * Time 16/9/5. * Description: */ public class ActivityAsyncTaskDiff extends Activity { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_async_diff); btn= (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new AysnTaskDiff("AysnTaskDiff-1").execute(""); new AysnTaskDiff("AysnTaskDiff-2").execute(""); new AysnTaskDiff("AysnTaskDiff-3").execute(""); new AysnTaskDiff("AysnTaskDiff-4").execute(""); new AysnTaskDiff("AysnTaskDiff-5").execute(""); }}); } private static class AysnTaskDiff extends AsyncTask<String ,Integer ,String>{ private String name; public AysnTaskDiff(String name){ super(); this.name=name; } @Override protected String doInBackground(String... params) { try { Thread.sleep(2000); }catch (Exception ex){ ex.printStackTrace(); } return name; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Logutils. e(s+" execute Completion time :"+df.format(new Date()))); }}}Copy the code
The case code is relatively simple, but more analysis, we directly look at the results of android 2.3.3 and Android 6.0, where Android 2.3.3 execute Log print as follows:
On Android 6.0, execute Log print as follows:
From printing logs, you can see that AsyncTask does execute tasks in parallel on Android 2.3.3, while it executes tasks in serial on Android 6.0. So what’s the use of knowing that? In fact, I only knew about this before, but recently I encountered the development problem of AsyncTask in SDK development. The scene of the problem is as follows. Our team uses AsyncTask as a network request class in SDK, because most systems are running on Android 3.0 or above now. Therefore, the default is serial operation. At the beginning, there was no problem when THE OVERSEAS VERSION of SDK was provided, until later when we provided a domestic Publisher for the overseas version, the problem appeared. After the publisher was connected to our SDK, the loading speed of their application network became very slow, and then they did not check out any problems. We’re in a tailspin… Until both of us find a point, that is, the Application of Publisher and our SDK both use AsyncTask as a network request, then the problem comes, our SDK triggers the network when the Application starts, and their Application also accesses the network when the Activity starts. So the SDK loads network data before the app, but!! AsyncTask is executed serially by default, so!! They didn’t start loading network data until our SDK network was loaded, which caused the network loading delay of the application to be very serious. Later, our SDK internally changed AsyncTask to parallel task and the problem was solved (of course, this is also a BUG of the SDK, which is not considered well). After Android 3.0 we can make AsyncTask perform parallel tasks with the following code, whose asynctask. THREAD_POOL_EXECUTOR is the internal thread pool of AsyncTask.
new AysnTaskDiff("AysnTaskDiff-5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
Copy the code
The first argument is passed to the thread pool, usually using the thread pool provided by AsyncTask (you can also create your own), and the second argument is the mutable argument that will eventually be passed to the doInBackground method. The implementation effect is not demonstrated, you can test yourself.
So far the differences between AsyncTask in different Android versions have been analyzed.
This article from: blog.csdn.net/javazejian/… If there is infringement, please contact to delete.