前言
今天我們來回顧複習下AsyncTask, 它是Android 一種輕量級的異步任務類,從實現來說,AsyncTask封裝了線程池和Handler。它可以在線程池中執行後臺任務,把執行的進度和結果傳遞給主線程並在主線程中更新UI,通過AsyncTask可以更加方便的執行後臺任務以及在主線程訪問UI,但他不適合進行特別耗時的後臺任務。
一、什麼是AsyncTask?
1、是一種輕量級的異步任務類;
2、是一個封裝了線程池和Handler的異步框架;
3、使用它可以更加方便的執行後臺任務以及在主線程訪問UI,但他不適合進行特別耗時的後臺任務;
二、AsyncTask的使用方法
1、3個參數
- Params:執行AsyncTask時,後臺任務需要傳入的參數,在doInBackground中取出使用。
- Progress:後臺任務執行時,如果需要顯示當前進度,則制定進度的數據類型。
- Result:後臺任務執行完成後的返回值類型。
2、5個方法
onPreExecute:
在主線程中執行,在後臺任務執行前調用,通常用於做一些準備操作。doInBackground(Params… params):
在線程池中執行,用於執行後臺任務;params 參數是 execute(Params… params)方法中傳遞的參數。在此方法中可以調用 publishProgress 方法來更新任務的進度,publishProgress方法會調用onProgressUpdate方法。publishProgress(Progress… values):
用於更新任務的進度,需要手動調用,publishProgress方法會調用onProgressUpdate方法;values參數爲設置的進度值。onProgressUpdate(Progress… values):
在主線程中執行,當後臺任務的執行進度發生改變時,會被調用,values參數爲進度值。onPostExecute(Result result):
在主線程中執行,當後臺任務執行完成時,會被調用。result 的值是doInBackground的返回值。
三、AsyncTask的機制原理
- AsyncTask 的本質是一個靜態的線程池,可以執行不同的異步任務,這些任務提交到靜態的線程池中執行。
- 線程池中的工作線程執行doInBackgroup(Params… params)方法執行異步任務。
- 當任務狀態改變後,工作線程向UI線程發送消息,AsyncTask內部的InternalHandler 響應這些消息,並調用相關的回調函數。
四、AsyncTask的注意事項
1、內存泄漏
原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放。
解決:
- AsyncTask內部持有外部Activity的弱引用。
- AsyncTask改爲靜態內部類。
- AsyncTask.cancel()。
2、生命週期
不受Activity生命週期的影響,在Activity銷燬之前,取消AsyncTask的運行,以此來保證程序的穩定。
3、結果丟失
由於屏幕旋轉、Activity在內存緊張時被回收等情況下,Activity會被重新創建,此時,還在運行的AsyncTask會持有一個Activity的非法引用即之前的Activity實例。導致onPostExecute()沒有任何作用。
4、並行or串行
- Android 1.6之前,默認採用串行執行任務;
- Android 1.6~ 2.3 ,默認採用並行執行任務;
- Android 3.0,默認採用串行執行任務,如果需要改爲並行,可以調用AsyncTask的executeOnExecutor()來執行任務即可。
5、AsyncTask的使用限制
- AsyncTask的對象必須在主線程中創建;
- execute 方法必須在UI線程調用;
- 不要在程序中手動調用 onPreExecute、onPostExecute、 doInBackground、onProgressUpdate方法;
- 一個AsyncTask對象只能調用一次excute()方法,執行一次,否則會報異常。
6、代碼實踐
模擬文件下載:
package com.example.ling.review.asynctask;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import java.lang.ref.WeakReference;
public class AsyncTaskActivity extends AppCompatActivity {
private DownLoadFileTask mTask = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 創建 DownLoadFileTask 實例
mTask = new DownLoadFileTask(this);
// 執行任務
mTask.execute("參數1", "參數2", "參數3");
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消 AsyncTask的任務
mTask.cancel(true);
}
/**
* 1、DownLoadFileTask 改爲靜態內部類,避免內存泄漏
* 2、將Activity 包裝成弱引用,避免內存泄漏
*/
public static class DownLoadFileTask extends AsyncTask<String, Integer, String> {
private WeakReference<AsyncTaskActivity> mWeakReference = null;
private ProgressDialog mProgressDialog = null;
private Context mContext = null;
public DownLoadFileTask(AsyncTaskActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
/**
* 運行在主線程,執行後臺任務之前調用,通常用來做一些準備操作
*/
@Override
protected void onPreExecute() {
mContext = mWeakReference.get();
if (mContext != null) {
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setCancelable(false);
mProgressDialog.setTitle("任務正在執行中");
mProgressDialog.setMessage("任務正在執行中,敬請期待...");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMax(200);
mProgressDialog.show();
}
}
/**
* 運行在線程池中,執行後臺耗時操作
*
* @param strings 參數
* @return 結果
*/
@Override
protected String doInBackground(String... strings) {
// 打印 mTask.execute();傳遞的參數
StringBuilder builder = new StringBuilder();
for (String string : strings) {
builder.append(string).append(",");
}
Log.d("doInBackground", builder.toString());
// 模擬下載
int progress = 0;
while (progress < 200) {
try {
Thread.sleep(1000); // 模擬耗時操作
progress += 10;
publishProgress(progress);// 更新進度,該方法調用後會觸發 onProgressUpdate方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
publishProgress(progress);
String msg = "文件下載成功!";
return msg;
}
/**
* 運行在主線程,用於更新進度,publishProgress方法執行後,會觸發該方法
*
* @param values 進度值
*/
@Override
protected void onProgressUpdate(Integer... values) {
if (mProgressDialog != null) {
mProgressDialog.setProgress(values[0]);
}
}
/**
* 運行在主線程,後臺任務執行完成後,將返回值傳給該方法
*
* @param s 返回的結果
*/
@Override
protected void onPostExecute(String s) {
if (mContext != null) {
mProgressDialog.dismiss();
Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
}
}
}
}
模擬文件下載:運行結果:
1、打印參數日誌:
01-28 10:25:56.385 5019-5089/com.example.ling.review D/doInBackground: 參數1,參數2,參數3,
2、運行界面效果