1. 程式人生 > >Android AsyncTask導致的記憶體洩漏

Android AsyncTask導致的記憶體洩漏

一般我們都認為,在一個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);
    }
}