Android中AsyncTask的內存泄露

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);
    }
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章