Android 日常開發(fā)的內(nèi)存泄漏與優(yōu)化

一.介紹

Android機器中,內(nèi)存使用問題一直是個十分重要,引人注目的問題,當我們代碼編寫不當,或者邏輯沒處理好,就會導致機器運行緩慢,有時候甚至死機。
對于程序員來說,這很致命,所以要去理解內(nèi)存的使用,去避免內(nèi)存的泄露,不斷優(yōu)化內(nèi)存,而當出現(xiàn)內(nèi)存泄露導致的問題,我們能夠分析log,并且會用工具MAT。

二.什么場景會導致內(nèi)存泄露

內(nèi)存泄露其實就是占用內(nèi)存的對象使用后沒有被回收。在這種現(xiàn)象下,當java程序運行一段時間,占用的內(nèi)存越來越大,導致該進程的內(nèi)存占用達到Android為進程分配的內(nèi)存使用上限,程序就死了。

  • ListView、GridView等在使用適配器時,沒有使用ConvertView緩存
  • Bitmap使用后沒有釋放
  • Context泄露。Context的引用超過了本身的生命周期。比如一個長時間在跑的異步任務或者長時間的對象,擁有著Activity(Context類型)的引用,這時Activity被銷毀了,但是內(nèi)存依然存在,Context無法被回收。所以這種情況下, 可以使用getApplicationContext比較好,或者弱引用Context
  • 數(shù)據(jù)庫游標或者文件流緩存等使用后未關(guān)閉
  • 線程使用不當。在線程中的run函數(shù)處理著耗時的工作,當設(shè)備橫豎屏切換,重新創(chuàng)建Activity,由于run函數(shù)未處理完,導致引用的Activity也不會被銷毀。再說AsyncTask,由于運行機制ThreadPoolExcutor,生命周期更加不可控,更容易出現(xiàn)問題了(具體解決方法,可看下面講解)。
  • Rxjava訂閱與反訂閱
  • BraodcastReceiver等在生命周期結(jié)束后一定要記得unregidter

三.內(nèi)存優(yōu)化注意點

1.圖片優(yōu)化

在Android中顯示Bitmap圖片,會造成一定的內(nèi)存消耗,甚至會導致OOM異常爆發(fā),所以對于圖片的顯示,要做一定的處理。

  • 大圖片顯示要進行壓縮才能加載。一張圖片,不要認為表面的小而不以為然,比如一張150kb的圖片,當讀到內(nèi)存時,若該圖片像素為20481024,使用屬性為ARGB_8888(默認),也就是一個像素4byte,那么總內(nèi)存就是42048*1024byte,8M多。可見,大圖片壓縮加載的必要性。具體壓縮詳細方法可見 Android高效加載大圖,防止OOM,以及多圖解決方案
  • 多圖顯示時要活用內(nèi)存緩存技術(shù)。在一個ListView(或GridView)中,不斷加載圖片,不可能一直把圖片都到內(nèi)存中,因為內(nèi)存是有上限值的,也要為其他操作分配內(nèi)存。所以當在可見區(qū)域里,要將移除屏幕的部分內(nèi)存進行回收處理??扇粢瞥牟糠衷谙聜€操作中又要馬上使用,這時若被移除回收,性能效率馬上就下去了,所以可以使用LRUCache緩存技術(shù)。具體可見以上那篇博文。
  • ListView中的快速滑動加載圖片,在不影響使用體驗的情況下,應判斷滑動狀態(tài)進行加載操作。在快速滑動時,由于滑動過程中加載的資源是不會被使用的,反而影響了用戶所要查看資源的加載,所以在快速滑動(SCROLL_STATE_TOUCH_SCROLL)列表時,就不再去獲取加載資源了,在靜止(SCROLL_STATE_IDLE)以及觸摸屏幕(SCROLL_STATE_TOUCH_SCROLL)時才去加載,我們需要注冊一個滾動監(jiān)聽器OnScrollListener 。
  • 由于圖片占用內(nèi)存的緣故,所以一些內(nèi)置資源的圖片可以經(jīng)過tinyPng處理,再放到app里加載。通常的壓縮率可以達到50%以上,意思是150kb的圖片處理后至少能減少到75kb以下,而且不失真。降低了內(nèi)存的占用,同時達到APK瘦身的效果。TinyPng入口地址

2.線程、異步任務優(yōu)化

因為線程、異步任務等生命周期的不可控性,成為了內(nèi)存泄露的另一個源頭。平常中,因為對它的頻繁使用,所以,我們應慎重對待它。

  • 在Activity結(jié)束時,應及時銷毀所創(chuàng)建的線程。不然,當線程持有該所在Activity的引用時,實際上以為退出去的Activity,其實由于線程未完成,所引用的老Activity是不會被銷毀的,就出現(xiàn)了內(nèi)存泄露,所以可使用Thread.interrupt()中斷線程,雖然并不是真正意義上的中斷!具體詳細可見 Thread的中斷機制(interrupt),這個機制到底做了些什么呢?原來Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,設(shè)置線程的中斷標示位,在線程受到阻塞的地方(如調(diào)用sleep、wait、join等地方)拋出一個異常InterruptedException,并且中斷狀態(tài)也將被清除,這樣線程就得以退出阻塞的狀態(tài)。
  • AsyncTask異步任務的生命周期不可控性。一定得注意一件事,因為以前很喜歡在Activity中創(chuàng)建AsyncTask作為內(nèi)部類,完成一些耗時且Ui交互的操作,十分方便,但是其實這個是具有很大風險的,因為很容易出現(xiàn)內(nèi)存泄露。異步任務內(nèi)部是以ThreadPoolExcutor作為實現(xiàn)機制的,這樣出來的線程對象生命周期是不確定的??!
    解決方案:在線程內(nèi)部采用弱引用保存Context引用。即如下代碼所示:
public abstract class WeakAsyncTask < Params,Progress,Result,WeakTarget > 
                 extends AsyncTask < Params,Progress,Result > {
    protected WeakReference < WeakTarget > mTarget;

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

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

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

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

    protected void onPreExecute(WeakTarget target) {
        // No default action  
    }

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

    protected void onPostExecute(WeakTarget target, Result result) {
        // No default action  
    }
}

所以內(nèi)存優(yōu)化方面,還有很多方面需要補足,還需努力。

四.內(nèi)存分析

不管是對內(nèi)存使用情況的分析,還是排查內(nèi)存泄露與溢出,我都推薦去好好看看以下大神的文章,相信看過后,會有很大的收獲,我就是如此=。=
android 中如何分析內(nèi)存泄露
Android最佳性能實踐(二)——分析內(nèi)存的使用情況

小小的總結(jié)與推薦閱讀,希望可以幫助到大家~
Ps:以前博客遷移到簡書,如果你覺得文章還可以,麻煩底下點個“喜歡”!

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

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

  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏大家都不陌生了,簡單粗俗的講,...
    宇宙只有巴掌大閱讀 2,483評論 0 12
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    _痞子閱讀 1,695評論 0 8
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,700評論 1 4
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    apkcore閱讀 1,303評論 2 7
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏大家都不陌生了,簡單粗俗的講,...
    DreamFish閱讀 863評論 0 5

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