AsyncTask也是個很常用的API,尤其在異步處理數據並將數據應用到視圖的操作場合,介紹如下:
- onPreExecute()
當任務執行之前開始調用此方法,通常在這裏顯示進度框等;
- doInBackground(Params...)
執行任務的時候調用此方法,在此方法內進行耗時的操作,後臺運行,在此方法中可以調用public Progress(Progress...)來更新進度;
- onProgressUpdate(Progress...)
此方法在主線程中執行,用於顯示當前執行任務的進度;
- onPostExecute(Result)
此方法在主線程執行,執行完畢的回調,並攜帶參數;
一般我們都認爲,在一個Activity中的AsyncTask它會隨着當前Activity的銷燬而銷燬,但事實並非如此,AsyncTask會在doInBackground()方法執行完畢之後再結束,所有有些猿人在進入到Activity之後快速的離開該頁面(前提是在異步中修改頁面佈局),此時App會很無情的給你Crash,一旦doInBackground()方法執行結束,會依據情況進行下一步的操作。但如果調用了cancle(boolean)方法,則會執行onCanclled(Result)方法,如果沒有調用,則自然而然的調用onPostExecute(Result)方法。
說明:cancle(boolean)方法的參數是一個boolean類型的,如果這個值爲true,說明當前任務可以打斷,調用該方法之後,打斷任務,並執行onCanclled(Result)方法,否則,正在執行的任務會繼續,知道完成任務爲止,再調用onPostExecute(Result)方法。如果在異步任務中有循環操作,我們就需要在循環中通過isCanclled()來進行判斷,當前任務是否已經被取消,如果返回true,我們應該避免後續無用的循環操作。但是如果在異步任務中有雷系BitmapFactory.decodeStream()的IO操作,調用cancle方法是無效的,沒有意義的,IO流會拋出異常信息,所以不建議使用AsyncTask這個API,可以使用Loader。
言歸正傳,來講下AsyncTask的內存泄露的問題,正如上面所說的,他不會隨着Activity的銷燬而銷燬,請看下面的一段示例代碼:
package com.tb.demo.utils.hangview;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
/**
* Created by tangbin on 15/9/6.
*/
public class SyncTaskDemoActivity extends Activity {
private int today = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 異步執行任務
new AsyncTask<Object, Void, Boolean>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Object... params) {
// do something in backfround
// 長時間的耗時
while (true) {
today++;
if (today > 100000)
break;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
// success do something
} else {
// error
}
}
}.execute();
}
}
然而,當我們在此異步任務還沒有執行完畢的時候去退出當前的這個Activity,此時,這個AsyncTask的生命週期比Activity要長,當Activity銷燬的時候,由於該異步任務持有該Activity的引用,導致Activity對象無法進行及時的回收,進而產生內存泄露的問題,而且,當while尚未執行完畢,該循環還有進行,浪費資源。
解決思路:在銷燬當前Activity的時候手動去調用AsyncTask的cancle方法
修改後的代碼如下:
package com.tb.demo.utils.hangview;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
/**
* Created by tangbin on 15/9/6.
*/
public class SyncTaskDemoActivity extends Activity {
private int today = 0;
private AsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAsyncTask = new AsyncTask<Object, Void, Boolean>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Object... params) {
// do something in backfround
// 長時間的耗時
while (true) {
if (cancel(true))
break;
today++;
if (today > 100000)
break;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
// success do something
} else {
// error
}
}
@Override
protected void onCancelled() {
super.onCancelled();
}
};
// 異步執行任務
mAsyncTask.execute();
}
@Override
protected void onDestroy() {
super.onDestroy();
mAsyncTask.cancel(true);
}
}