Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中使用Thread造成內存洩露的分析和解決

Android中使用Thread造成內存洩露的分析和解決

編輯:關於Android編程

Thread 內存洩露

線程也是造成內存洩露的一個重要的源頭。線程產生內存洩露的主要原因在於線程生命周期的不可控。

看一下下面是否存在問題

    public class ThreadActivity extends Activity {
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new MyThread().start();
        }

        private class MyThread extends Thread {
            @Override
            public void run() {
                super.run();
                dosomthing();
            }
        }
        private void dosomthing(){

        }
    }

這段代碼很平常也很簡單,是我們經常使用的形式。


真的沒有問題嗎

我們思考一個問題:假設MyThread的run函數是一個很費時的操作,當我們開啟該線程後,將設備的橫屏變為了豎屏,
一般情況下當屏幕轉換時會重新創建Activity,按照我們的想法,老的Activity應該會被銷毀才對,然而事實上並非如此。
由於我們的線程是Activity的內部類,所以MyThread中保存了Activity的一個引用,當MyThread的run函數沒有結束時,
MyThread是不會被銷毀的,因此它所引用的老的Activity也不會被銷毀,因此就出現了內存洩露的問題。

這種線程導致的內存洩露問題應該如何解決呢?

  1. 將線程的內部類,改為靜態內部類。
  2. 在線程內部采用弱引用保存Context引用。

代碼如下:

public class ThreadAvoidActivity extends Activity {
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                new MyThread(this).start();
            }

            private void dosomthing() {

            }

            private static class MyThread extends Thread {
                WeakReference mThreadActivityRef;

                public MyThread(ThreadAvoidActivity activity) {
                    mThreadActivityRef = new WeakReference(
                            activity);
                }

                @Override
                public void run() {
                    super.run();
                    if (mThreadActivityRef == null)
                        return;
                    if (mThreadActivityRef.get() != null)
                        mThreadActivityRef.get().dosomthing();
                    // dosomthing
                }
            }
        }

上面的兩個步驟其實是切換兩個對象的雙向強引用鏈接
靜態內部類:切斷Activity 對於 MyThread的強引用。
弱引用: 切斷MyThread對於Activity 的強引用。

AsynTask 內部類會如何呢?

有些人喜歡用Android提供的AsyncTask,但事實上AsyncTask的問題更加嚴重,
Thread只有在run函數不結束時才出現這種內存洩露問題,然而AsyncTask內部的實現機制是運用了ThreadPoolExcutor,
該類產生的Thread對象的生命周期是不確定的,是應用程序無法控制的,
因此如果AsyncTask作為Activity的內部類,就更容易出現內存洩露的問題。

在看android聯系人2.3源碼的時候看到一個類WeakAsyncTask, 內部用到了WeakReference軟引用, 這樣可以解決內存洩露的問題; 其實給AysncTask加上static, 靜態的內部類不會持有對外部類的引用, 就能夠解決問題, 只不過這樣如果AysncTask的方法用到的成員變量都需要加上static;

WeakReference和AsyncTask的美妙結合

為了避免開發者在UI線程上做耗時操作,Android提供了不少異步API,其中之一就是AsyncTask。而對於某些頻繁操作mysql" target="_blank" title="MySQL知識庫">數據庫的應用(例如,Phonebook)而言,需要一種異步的並且低耗資源的(低耗是兩個方面的事情,要麼是你占有的多點,但是能快速釋放;要麼是你本身就占有的少。這兩種都可以保證其它應用有資源可用)組件。所以,那就來個WeakAsyncTask吧,唔,美妙的產物,既保證占有資源的快速釋放,又保證操作是異步進行。
那為什麼不是soft reference呢?
這個,來看看weak和soft兩者的區別:

* A SoftReference should be cleared and enqueued as late as possible, that is, in case the VM is in danger of running out of memory.
* A WeakReference may be cleared and enqueued as soon as is known to be weakly-referenced.

下面是WeakAsyncTask的源碼:

public abstract class WeakAsyncTask
    extends AsyncTask {
protected WeakReference mTarget;

public WeakAsyncTask(WeakTarget target) {
    mTarget = new WeakReference(target);
}

@Override
protected final void onPreExecute() {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        this.onPreExecute(target);
    }
}

@Override
protected final Result doInBackground(Params... params) {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        return this.doInBackground(target, params);
    } else {
        return null;
    }
}

@Override
protected final void onPostExecute(Result result) {
    final WeakTarget target = mTarget.get();
    if (target != null) {
        this.onPostExecute(target, result);
    }
}

protected void onPreExecute(WeakTarget target) {
    // Nodefaultaction
}

protected abstract Result doInBackground(WeakTarget target,
        Params... params);

protected void onPostExecute(WeakTarget target, Result result) {
    // Nodefaultaction
}
}

使用也很簡單, 用法也幾乎和AsyncTask一樣:

private static class MyTask extends WeakAsyncTask {
        public MyTask(MainActivity target) {
            super(target);
        }

        @Override
        protected String doInBackground(MainActivity target, Void... params) { // 獲取context,
            // 執行一些操作
            Context context = target;
            return "Hello Android    !!!!!";
        }

        @Override
        protected void onPostExecute(MainActivity target, String s) {
            // 執行操作
        }
    }

直接調用執行:

new MyTask(this).execute(); 

其實還可以根據這個思想寫一個WeakHandler!
 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved