Android內(nèi)存泄漏及解決方法

背景

在Android開(kāi)發(fā)中內(nèi)存泄漏是一個(gè)相對(duì)來(lái)說(shuō)比較常見(jiàn)的問(wèn)題,這個(gè)問(wèn)題也相當(dāng)嚴(yán)重,但是有好多朋友還不知道怎么解決和查看內(nèi)存泄漏問(wèn)題,這里就寫(xiě)一篇文章來(lái)給大家介紹一些常見(jiàn)的內(nèi)存泄漏問(wèn)題以及解決方法,如果想看內(nèi)存泄漏檢測(cè)的可以看這里 Android內(nèi)存泄漏檢測(cè)

常見(jiàn)內(nèi)存泄漏

1. 靜態(tài)引用

比如以下代碼,定義了sInstance來(lái)傳遞和使用,會(huì)導(dǎo)致MainActivity無(wú)法被銷毀,這是一種比較低級(jí)的錯(cuò)誤,一般我們不建議這么使用,如果一定要使用,就需要在最后將sInstance置空。

public class MainActivity extends Activity {
    private static Activity sInstance;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sInstance = this;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        sInstance = null;
    }
}

2. 內(nèi)部類持有外部類的引用,內(nèi)部類被靜態(tài)引用,等同于外部類被靜態(tài)引用

在JAVA中內(nèi)部類的定義與使用一般為成員內(nèi)部類與匿名內(nèi)部類,他們的對(duì)象都會(huì)隱式持有外部類對(duì)象的引用,影響外部類對(duì)象的回收。
GC只會(huì)回收沒(méi)有被引用或者根集不可到達(dá)的對(duì)象(取決于GC[算法]),內(nèi)部類在生命周期內(nèi)始終持有外部類的對(duì)象的引用,造成外部類的對(duì)象始終不滿足GC的回收條件,反映在內(nèi)存上就是內(nèi)存泄露。(如,[Android]中Activity的內(nèi)存泄露)
解決方案為
1.將內(nèi)部類定義為static
2.用static的變量引用匿名內(nèi)部類的實(shí)例或?qū)⒛涿麅?nèi)部類的實(shí)例化操作放到外部類的靜態(tài)方法中

public class MainActivity extends Activity {
    private static TestClass sTestClass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    public class TestClass{

    }
}

3. AsyncTask

下面的代碼我們?cè)谕顺龅臅r(shí)候mAsyncTask是無(wú)法被銷毀的,會(huì)導(dǎo)致Activity無(wú)法被銷毀:

private AsyncTask mAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAsyncTask = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                while (true);
            }
        };
        mAsyncTask.execute();
    }

解決辦法如下代碼:在Activity銷毀的時(shí)候取消正在運(yùn)行的AsyncTask

public class MainActivity extends Activity {
    private AsyncTask mAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAsyncTask = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                while (true){
                    if(isCancelled()){
                        break;
                    }
                }
                return null;
            }
        };
        mAsyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != mAsyncTask && !mAsyncTask.isCancelled()){
            mAsyncTask.cancel(true);
        }
        mAsyncTask = null;
    }
}

4. Handle造成的內(nèi)存泄漏

4.1 用過(guò)內(nèi)部類創(chuàng)建的Handler對(duì)象,持有Activity的引用。當(dāng)執(zhí)行postDelayed()方法時(shí),會(huì)把Handler裝入一個(gè)Message,并把Message推到MessageQueue中,MessageQueue在一個(gè)Looper線程中不斷輪詢處理消息。
因?yàn)檠舆t的時(shí)間足夠長(zhǎng),當(dāng)Activity退出時(shí),消息隊(duì)列還未處理Message,從而引發(fā)OOM

public class MainActivity extends Activity {
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendEmptyMessageDelayed(0,1000);
    }
}

那么對(duì)于上面產(chǎn)生的內(nèi)存泄漏我們要怎么處理呢?采用弱引用的形式:

public class MainActivity extends Activity {
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(new WeakReference<Activity>(this));
        mHandler.sendEmptyMessageDelayed(0,1000);

    }

    public static class MyHandler extends Handler{
        private WeakReference<Activity> mActivity;
        public MyHandler(WeakReference<Activity> activityWeakReference){
            mActivity = activityWeakReference;
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(null != mActivity){
                Activity activity = mActivity.get();
                if(null != activity && !activity.isFinishing()){
                    //TODO do something...
                }
            }
        }
    }
}

4.2 內(nèi)部調(diào)用Handler,解決辦法,在Activity銷毀的時(shí)候?qū)andler里面的信息清除。

public class MainActivity extends Activity {
    private Handler mHandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        },1000);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

5. Thread引起的內(nèi)存泄漏

下面的方法會(huì)產(chǎn)生內(nèi)存泄漏,相信大概都知道,那么我們應(yīng)該怎么去解決呢?

public class MainActivity extends Activity {
    private Thread mThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mThread = new Thread(){
            @Override
            public void run() {
                super.run();
                while (true){

                }
            }
        };
        mThread.start();
    }
}

通過(guò)前面幾種內(nèi)存泄漏的解決方法,我相信大家應(yīng)該也能猜出個(gè)大概,無(wú)非就是在Activity銷毀的時(shí)候去暫停Thread那么我們應(yīng)該怎么做呢?

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != mThread){
            mThread.interrupt();
        }
    }

interrupt()和Thread.interrupt()的區(qū)別,其中interrupt()是作用于調(diào)用線程的,比如我們上面調(diào)用的,他是作用于mThread這個(gè)線程的,如果我們?cè)谏厦媸褂肨hread.interrupt()那么就是作用于主線程的。

6. WebView造成的內(nèi)存泄漏

WebView在解析網(wǎng)頁(yè)時(shí),會(huì)申請(qǐng)Native堆內(nèi)存,保存頁(yè)面元素(圖片、history等)

7. 其他原因

BraodcastReceiver
ContentObserver
File
Cursor
Stream
Bitmap
Timer

總結(jié)

上面是我在平時(shí)開(kāi)發(fā)中遇到的一些關(guān)于內(nèi)存泄漏的地方,大家有什么其他的內(nèi)存泄漏或者對(duì)于以上有更好的解決方法,歡迎留言。樓主還寫(xiě)了一篇關(guān)于 Android內(nèi)存泄漏檢測(cè)的有興趣的同學(xué)也可以看一下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏大家都不陌生了,簡(jiǎn)單粗俗的講,...
    宇宙只有巴掌大閱讀 2,492評(píng)論 0 12
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏...
    apkcore閱讀 1,310評(píng)論 2 7
  • 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏大家都不陌生了,簡(jiǎn)單粗俗的講,...
    DreamFish閱讀 874評(píng)論 0 5
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏...
    _痞子閱讀 1,703評(píng)論 0 8
  • 最近搞游戲的項(xiàng)目,結(jié)識(shí)了不少新的朋友外對(duì)游戲圈有了一些認(rèn)識(shí)。 可以這么說(shuō),在iphone推出來(lái)的頭幾年我還是玩了一...
    ouzar閱讀 323評(píng)論 0 0

友情鏈接更多精彩內(nèi)容