Android 性能優(yōu)化之內(nèi)存泄漏檢測(cè)以及內(nèi)存優(yōu)化(下)

上篇博客我們寫到了 Android 中內(nèi)存泄漏的檢測(cè)以及相關(guān)案例惩猫,這篇我們繼續(xù)來分析一下 Android 內(nèi)存優(yōu)化的相關(guān)內(nèi)容。
  上篇:Android 性能優(yōu)化之內(nèi)存泄漏檢測(cè)以及內(nèi)存優(yōu)化(上)
  中篇:Android 性能優(yōu)化之內(nèi)存泄漏檢測(cè)以及內(nèi)存優(yōu)化(中)酱固。
  下篇:Android 性能優(yōu)化之內(nèi)存泄漏檢測(cè)以及內(nèi)存優(yōu)化(下)携龟。
  轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/self_study/article/details/68946441
  對(duì)技術(shù)感興趣的同鞋加群544645972一起交流。

QQ圖片20170426100405.png

Android 內(nèi)存優(yōu)化

上篇博客描述了如何檢測(cè)和處理內(nèi)存泄漏衡创,這種問題從某種意義上講是由于代碼的錯(cuò)誤導(dǎo)致的帝嗡,但是也有一些是代碼沒有錯(cuò)誤,但是我們可以通過很多方式去降低內(nèi)存的占用璃氢,使得應(yīng)用的整體內(nèi)存處于一個(gè)健康的水平哟玷,下面總結(jié)一下內(nèi)存優(yōu)化的幾個(gè)點(diǎn):

圖片處理優(yōu)化

由于圖片在應(yīng)用中使用的較為頻繁,而且圖片占用的內(nèi)存通常來說也比較大一也,舉個(gè)例子來說巢寡,現(xiàn)在正常的手機(jī)基本都在 1000W 像素左右的水平,較好的基本都在 1600W 像素塘秦,這時(shí)候拍出來的照片基本都在 3400*4600 這個(gè)水平讼渊,按照 ARGB_8888 的標(biāo)準(zhǔn),一個(gè)像素 4 個(gè)字節(jié)尊剔,所以總共有 1600W*4=6400W 字節(jié)爪幻,總共 64M,也就是說會(huì)占用 64M 的內(nèi)存须误,而實(shí)際出來的 .png 圖片大小也就才 3M 左右昌妹,這是一個(gè)非橙哂龋恐怖的數(shù)量,因?yàn)閷?duì)于一個(gè) 2G 左右內(nèi)存的手機(jī)來說,一個(gè)進(jìn)程最大可用的內(nèi)存可能也就在 100M+互艾,一張圖片就能夠占用一半內(nèi)存,這也就是為什么 decode 一個(gè) bitmap 是發(fā)生 OOM 高頻的地方泥张,所以在實(shí)際開發(fā)過程中圖片的處理和內(nèi)存占用優(yōu)化也是一個(gè)比較重要的地方。
  Android中圖片有四種屬性疲陕,分別是:<ul><li>ALPHA_8:每個(gè)像素占用1byte內(nèi)存 </li><li>ARGB_4444:每個(gè)像素占用2byte內(nèi)存 </li><li>ARGB_8888:每個(gè)像素占用4byte內(nèi)存 (默認(rèn))</li><li>RGB_565:每個(gè)像素占用2byte內(nèi)存</li></ul>

大圖片優(yōu)化

為了找出在運(yùn)行過程中占用內(nèi)存很大的圖片,這個(gè)時(shí)候就可以借助上篇博客介紹到的 MAT 了钉赁,按照 Retained Heap 大小進(jìn)行排序蹄殃,找出占用內(nèi)存比較大的幾個(gè)對(duì)象,然后通過引用鏈找到持有它的地方你踩,最后看能否有優(yōu)化的地方诅岩。

圖片分辨率相關(guān)

我們一般將不同分辨率的圖片放置在不同的文件夾 hdpi/xhdpi/xxhdpi 下面進(jìn)行適配,通過 android:background 來設(shè)置背景圖片或者使用 BitmapFactory.decodeResource() 方法的時(shí)候带膜,圖片默認(rèn)情況下會(huì)進(jìn)行縮放吩谦,在 Java 層實(shí)際調(diào)用的是 BitmapFactory 里的 decodeResourceStream 方法:

/**
 * Decode a new Bitmap from an InputStream. This InputStream was obtained from
 * resources, which we pass to be able to scale the bitmap accordingly.
 */
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
        InputStream is, Rect pad, Options opts) {

    if (opts == null) {
        opts = new Options();
    }

    if (opts.inDensity == 0 && value != null) {
        final int density = value.density;
        if (density == TypedValue.DENSITY_DEFAULT) {
            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
        } else if (density != TypedValue.DENSITY_NONE) {
            opts.inDensity = density;
        }
    }
    
    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    }
    
    return decodeStream(is, pad, opts);
}

decodeResourceStream 在解析時(shí)會(huì)將 Bitmap 根據(jù)當(dāng)前設(shè)備屏幕像素密度 densityDpi 的值進(jìn)行縮放適配操作,使得解析出來的 Bitmap 與當(dāng)前設(shè)備的分辨率匹配膝藕,達(dá)到一個(gè)最佳的顯示效果式廷,上面也提到過,解析過后 Bitmap 的大小將比原始的大不少束莫,關(guān)于 Bitmap 的詳細(xì)分析可以看一下這篇博客:Android 開發(fā)繞不過的坑:你的 Bitmap 究竟占多大內(nèi)存懒棉?
  關(guān)于 Density览绿、分辨率和相關(guān) res 目錄的關(guān)系如下:

DensityDpi 分辨率 res Density
160dpi 320 x 533 mdpi 1
240dpi 460 x 800 hdpi 1.5
320dpi 720 x 1280 xhdpi 2
480dpi 1080 x 1920 xxhdpi 3
560dpi 1440 x 2560 xxxhdpi 3.5

舉個(gè)例子來說一張 1920x1080 的圖片來說策严,如果放在 xhdpi 下面,那么 xhdpi 設(shè)備將其轉(zhuǎn)成 bitmap 之后的大小是 1920x1080饿敲,而 xxhdpi 設(shè)備獲取的大小則是 2520x1418妻导,大小約為前者的 1.7 倍,這些內(nèi)存對(duì)于移動(dòng)設(shè)備來說已經(jīng)算是比較大的差距怀各。有一點(diǎn)需要提到的是新版本 Android Studio 已經(jīng)使用 mipmap 來代替了倔韭,比起 drawable 官方的解釋是系統(tǒng)會(huì)在縮放上提供一定的性能優(yōu)化:

Mipmapping for drawables

Using a mipmap as the source for your bitmap or drawable is a simple way to provide a quality image and various image scales, which can be particularly useful if you expect your image to be scaled during an animation.

Android 4.2 (API level 17) added support for mipmaps in the Bitmap class—Android swaps the mip images in your Bitmap when you've supplied a mipmap source and have enabled setHasMipMap(). Now in Android 4.3, you can enable mipmaps for a BitmapDrawable object as well, by providing a mipmap asset and setting the android:mipMap attribute in a bitmap resource file or by calling hasMipMap().

但是從用法來說和正常的 drawable 一樣。
  系統(tǒng)也對(duì)圖片展示進(jìn)行了相應(yīng)的優(yōu)化瓢对,對(duì)于類似在 xml 里面直接通過 android:background 或者 android:src 設(shè)置的背景圖片寿酌,以 ImageView 為例,最終會(huì)調(diào)用 ResourceImpl(低版本是 Resource) 類中的里的 loadDrawable 方法硕蛹,在這個(gè)方法中我們可以很清楚的看到系統(tǒng)針對(duì)相同的圖片使用享元模式構(gòu)造了一個(gè)全局的緩存 DrawableCache 類的對(duì)象:

Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
        boolean useCache) throws NotFoundException {
    try {
        if (TRACE_FOR_PRELOAD) {
            // Log only framework resources
            if ((id >>> 24) == 0x1) {
                final String name = getResourceName(id);
                if (name != null) {
                    Log.d("PreloadDrawable", name);
                }
            }
        }

        final boolean isColorDrawable;
        final DrawableCache caches;
        final long key;
        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            isColorDrawable = true;
            caches = mColorDrawableCache;
            key = value.data;
        } else {
            isColorDrawable = false;
            caches = mDrawableCache;
            key = (((long) value.assetCookie) << 32) | value.data;
        }

        // First, check whether we have a cached version of this drawable
        // that was inflated against the specified theme. Skip the cache if
        // we're currently preloading or we're not using the cache.
        if (!mPreloading && useCache) {
            final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
            if (cachedDrawable != null) {
                return cachedDrawable;
            }
        }
        .....
}

DrawableCache 類繼承自 ThemedResourceCache 類醇疼,來看看這兩個(gè)相關(guān)類:

/**
 * Class which can be used to cache Drawable resources against a theme.
 */
class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
    ......
}
/**
 * Data structure used for caching data against themes.
 *
 * @param <T> type of data to cache
 */
abstract class ThemedResourceCache<T> {
    private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
    private LongSparseArray<WeakReference<T>> mUnthemedEntries;
    private LongSparseArray<WeakReference<T>> mNullThemedEntries;
    .....
}

可以看到這個(gè)類使用一個(gè) ArrayMap 來存儲(chǔ)一個(gè) Drawable 和這個(gè) Drawable 對(duì)應(yīng)的 Drawable.ConstantState 信息,相同的圖片對(duì)應(yīng)相同的 Drawable.ConstantState法焰,所以這就可以保證在一些情況下相同的圖片系統(tǒng)只需要保存一份秧荆,從而減少內(nèi)存占用。我們從這里可以得到一些啟示埃仪,如果我們?cè)谀承?huì)重復(fù)使用圖片的場(chǎng)景下乙濒,自己構(gòu)造一個(gè) Bitmap 緩存器,然后里面保存 Bitmap 的 WeakReference卵蛉,當(dāng)使用的時(shí)候先去緩存里面獲取颁股,獲取不到再做解析的操作么库。

圖片壓縮

BitmapFactory 在 decode 圖片的時(shí)候,可以帶上一個(gè) Options豌蟋,這個(gè)很多人應(yīng)該很熟悉廊散,在 Options 中我們可以指定使用一些壓縮的功能:<ul><li> inTargetDensity </li>表示要被畫出來時(shí)的目標(biāo)像素密度;<li> inSampleSize </li> 這個(gè)值是一個(gè) int梧疲,當(dāng)它小于 1 的時(shí)候,將會(huì)被當(dāng)做 1 處理运准,如果大于 1幌氮,那么就會(huì)按照比例(1 / inSampleSize)縮小 bitmap 的寬和高、降低分辨率胁澳,大于 1 時(shí)這個(gè)值將會(huì)被處置為 2 的指數(shù)(3 會(huì)被處理為 4该互,5被處理為8)。例如 width=100韭畸,height=100宇智,inSampleSize=2,那么就會(huì)將 bitmap 處理為胰丁,width=50随橘,height=50,寬高降為 1/2锦庸,像素?cái)?shù)降為 1/4机蔗;<li> inJustDecodeBounds </li>字面意思就可以理解就是只解析圖片的邊界,有時(shí)如果只是為了獲取圖片的大小就可以用這個(gè)甘萧,而不必直接加載整張圖片萝嘁;<li> inPreferredConfig </li>默認(rèn)會(huì)使用 ARGB_8888,在這個(gè)模式下一個(gè)像素點(diǎn)將會(huì)占用 4 個(gè)字節(jié)扬卷,而對(duì)一些沒有透明度要求或者圖片質(zhì)量要求不高的圖片牙言,可以使用 RGB_565,這樣一個(gè)像素只會(huì)占用 2 個(gè)字節(jié)怪得,一下就可以省下 50% 內(nèi)存了咱枉;<li>inPurgeable 和 inInputShareable</li>這兩個(gè)需要一起使用,BitmapFactory 類的源碼里面有注釋汇恤,大致意思是表示在系統(tǒng)內(nèi)存不足時(shí)是否可以回收這個(gè) Bitmap庞钢,有點(diǎn)類似軟引用,但是實(shí)際在 5.0 以后這兩個(gè)屬性已經(jīng)被忽略因谎,因?yàn)橄到y(tǒng)認(rèn)為回收后再解碼實(shí)際反而可能會(huì)導(dǎo)致性能問題基括;<li> inBitmap </li>官方推薦使用的參數(shù),表示重復(fù)利用圖片內(nèi)存财岔,減少內(nèi)存分配风皿,在 4.4 以前只有相同大小的圖片內(nèi)存區(qū)域可以復(fù)用河爹,4.4 以后只要原有的圖片比將要解碼的圖片大就可以實(shí)現(xiàn)復(fù)用了。</ul>  關(guān)于圖片壓縮和圖片內(nèi)存優(yōu)化的例子可以參考我以前寫的一個(gè)博客:android仿最新版本微信相冊(cè)--附源碼桐款。

巨型圖片的處理

要加載一張巨型的圖片咸这,比如 20000*10000 分辨率的,這個(gè)時(shí)候全放進(jìn)內(nèi)存是完全不可能的魔眨,直接會(huì)占用 800M 內(nèi)存媳维,所以必須要用到上面說到的壓縮比,將其分辨率降低到和屏幕匹配遏暴,匹配之后如果還要去支持用戶的放大侄刽、縮小、左右滑動(dòng)等操作朋凉,這時(shí)候就可以使用 BitmapRegionDecoder 這個(gè)類去處理圖片了州丹,具體的可以去看看這篇博客:Android 高清加載巨圖方案 拒絕壓縮圖片,實(shí)現(xiàn)的原理就是分區(qū)域去加載杂彭,或者可以去參考這個(gè)開源庫:WorldMap墓毒。

圖片緩沖池

現(xiàn)在默認(rèn)的圖片加載工具例如 Universal-ImageLoader 或者 Glide 都會(huì)使用一個(gè) LruCache 來管理應(yīng)用中的圖片緩存,一般緩沖池的大小設(shè)置為應(yīng)用可用內(nèi)存的 1/8亲怠。

有效利用系統(tǒng)自帶資源

Android 系統(tǒng)本身內(nèi)置了大量的資源所计,比如一些通用的字符串、顏色定義赁炎、常用 icon 圖片醉箕,還有些動(dòng)畫和頁面樣式以及簡(jiǎn)單布局,如果沒有特別的要求徙垫,這些資源都可以在應(yīng)用程序中直接引用讥裤。直接使用系統(tǒng)資源不僅可以在一定程度上減少內(nèi)存的開銷,還可以減少應(yīng)用程序 APK 的體積:<ul><li>利用系統(tǒng)定義的 ID</li>比如我們有一個(gè)定義 ListView 的 xml 文件姻报,一般的己英,我們會(huì)寫類似下面的代碼片段:

<ListView  
    android:id="@+id/mylist"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"/>  

這里我們定義了一個(gè) ListView,定義它的 id 是 "@+id/mylist"吴旋,實(shí)際上损肛,如果沒有特別的需求,就可以利用系統(tǒng)定義的 ID荣瑟,類似下面的樣子:

<ListView  
    android:id="@android:id/list"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"/>  

在 xml 文件中引用系統(tǒng)的 ID治拿,只需要加上 “@android:” 前綴即可。如果是在Java代碼中使用系統(tǒng)資源笆焰,和使用自己的資源基本上是一樣的劫谅。不同的是,需要使用 android.R 類來使用系統(tǒng)的資源,而不是使用應(yīng)用程序指定的 R 類捏检。這里如果要獲取 ListView 可以使用 android.R.id.list 來獲溶衤俊;
<li>利用系統(tǒng)的圖片資源</li>這樣做的好處贯城,一個(gè)是美工不需要重復(fù)的做一份已有的圖片了熊楼,可以節(jié)約不少工時(shí),另一個(gè)是能保證我們的應(yīng)用程序的風(fēng)格與系統(tǒng)一致能犯;<li>利用系統(tǒng)的字符串資源</li>如果使用系統(tǒng)的字符串鲫骗,默認(rèn)就已經(jīng)支持多語言環(huán)境了,直接去使用 @android:string/yes 和 @android:string/no悲雳,在簡(jiǎn)體中文環(huán)境下會(huì)顯示“確定”和“取消”挎峦,在英文環(huán)境下會(huì)顯示 “OK” 和 “Cancel”;<li>利用系統(tǒng)的 Style</li>假設(shè)布局文件中有一個(gè) TextView合瓢,用來顯示窗口的標(biāo)題,使用中等大小字體透典,可以使用下面的代碼片段來定義 TextView 的 Style:

<TextView  
    android:id="@+id/title"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:textAppearance="?android:attr/textAppearanceMedium" />  

其中 android:textAppearance="?android:attr/textAppearanceMedium" 就是使用系統(tǒng)的 style晴楔,需要注意的是使用系統(tǒng)的 style 必須在想要使用的資源前面加 “?android:” 作為前綴,而不是 “@android:”峭咒;
<li>利用系統(tǒng)的顏色定義</li>除了上述的各種系統(tǒng)資源以外税弃,還可以使用系統(tǒng)定義好的顏色,在項(xiàng)目中最常用的凑队,就是透明色的使用 android:background ="@android:color/transparent" 则果。</ul>

內(nèi)存抖動(dòng)造成內(nèi)存碎片優(yōu)化

上篇博客說到過頻繁的 GC 會(huì)造成內(nèi)存的抖動(dòng),最終會(huì)導(dǎo)致內(nèi)存當(dāng)中存在很多內(nèi)存碎片漩氨,雖然總體來說內(nèi)存是可用的西壮,但是當(dāng)分配內(nèi)存給一個(gè)大對(duì)象的時(shí)候,沒有一塊足夠大的連續(xù)區(qū)域可以分配給這個(gè)對(duì)象就會(huì)造成 OOM叫惊,所以這個(gè)時(shí)候?yàn)榱藴p少內(nèi)存抖動(dòng)款青,需要去觀察 Memory Monitor,檢查應(yīng)用的正常使用過程中有沒有因?yàn)轭l繁的內(nèi)存分配和釋放導(dǎo)致鋸齒形狀的內(nèi)存圖霍狰,如果有的話去檢查相關(guān)代碼抡草,比較容易出現(xiàn)內(nèi)存抖動(dòng)的地方可能是 convertView 沒有復(fù)用、頻繁拼接小的 String 字符串蔗坯、在 for 循環(huán)中創(chuàng)建對(duì)象等等康震,找到問題所在,解決內(nèi)存抖動(dòng)宾濒。

常用數(shù)據(jù)結(jié)構(gòu)優(yōu)化

ArrayMap 以及 SparseArray 是 Android 系統(tǒng)專門為移動(dòng)設(shè)備而定制的數(shù)據(jù)結(jié)構(gòu)腿短,用于在一定情況下取代 HashMap 而達(dá)到節(jié)省內(nèi)存的目的,對(duì)于 key 為 int 的 HashMap 盡量使用 SparceArray 替代(一般 Lint 也會(huì)提示開發(fā)者將其換成 SparceArray),大概可以省30%的內(nèi)存答姥,而對(duì)于其他類型铣除,ArrayMap 對(duì)內(nèi)存的節(jié)省實(shí)際并不明顯,10% 左右鹦付,但是數(shù)據(jù)量在 1000 以上時(shí)尚粘,查找速度可能會(huì)變慢,具體的可以看看這篇博客:HashMap敲长,ArrayMap郎嫁,SparseArray源碼分析及性能對(duì)比

避免創(chuàng)建不必要的對(duì)象

最常見的例子就是當(dāng)你要頻繁操作一個(gè)字符串時(shí)祈噪,使用 StringBuilder 代替 String泽铛。對(duì)于所有基本類型的組合:int 數(shù)組比 Integer 數(shù)組好,這也概括了一個(gè)基本事實(shí)辑鲤,兩個(gè)平行的 int 數(shù)組比 (int,int) 對(duì)象數(shù)組性能要好很多盔腔。總體來說月褥,就是避免創(chuàng)建短命的臨時(shí)對(duì)象弛随。減少對(duì)象的創(chuàng)建就能減少垃圾收集,進(jìn)而減少對(duì)用戶體驗(yàn)的影響宁赤。

盡量避免使用枚舉

Android 平臺(tái)上枚舉是比較爭(zhēng)議的舀透,在較早的 Android 版本,使用枚舉會(huì)導(dǎo)致包過大决左,在某些情況下使用枚舉甚至比直接使用 int 包的 size 大了 10 多倍愕够。在 Stackoverflow 上也有很多的討論,大致意思是隨著虛擬機(jī)的優(yōu)化佛猛,目前枚舉變量在 Android 平臺(tái)性能問題已經(jīng)不大惑芭,而目前 Android 官方建議,使用枚舉變量還是需要謹(jǐn)慎挚躯,因?yàn)槊杜e變量可能比直接用 int 多使用 2 倍的內(nèi)存强衡,具體的可以看看這個(gè)討論:Should I strictly avoid using enums on Android?

盡量使用系統(tǒng)類庫

選擇系統(tǒng)類庫中的代碼而非自己重寫,第一個(gè)可以節(jié)省少部分內(nèi)存码荔,第二個(gè)考慮到系統(tǒng)空閑時(shí)會(huì)用匯編代碼調(diào)用來替代系統(tǒng)類庫中方法漩勤,這可能比 JIT 中生成的等價(jià)的最好的 Java 代碼還要快:<ul><li>當(dāng)你在處理字串的時(shí)候,不要吝惜使用 String.indexOf()缩搅,String.lastIndexOf() 等特殊實(shí)現(xiàn)的方法越败,這些方法都是使用 C/C++ 實(shí)現(xiàn)的,比起 Java 循環(huán)快 10 到 100 倍硼瓣;</li><li>System.arraycopy 方法在有 JIT 的 Nexus One 上究飞,自行編碼的循環(huán)快 9 倍置谦;</li><li>android.text.format 包下的 Formatter 類,提供了 IP 地址轉(zhuǎn)換亿傅、文件大小轉(zhuǎn)換等方法媒峡,DateFormat 類,提供了各種時(shí)間轉(zhuǎn)換葵擎,都是非常高效的方法谅阿;</li><li>TextUtils 類,對(duì)于字符串處理 Android 為我們提供了一個(gè)簡(jiǎn)單實(shí)用的 TextUtils 類酬滤,如果處理比較簡(jiǎn)單的內(nèi)容不用去思考正則表達(dá)式不妨試試這個(gè) android.text.TextUtils 的類签餐;</li><li>高性能 MemoryFile 類,很多人抱怨 Android 處理底層 I/O 性能不是很理想盯串,如果不想使用 NDK 則可以通過 MemoryFile 類實(shí)現(xiàn)高性能的文件讀寫操作氯檐。MemoryFile 適用于哪些地方呢?對(duì)于 I/O 需要頻繁操作的体捏,主要是和外部存儲(chǔ)相關(guān)的 I/O 操作冠摄,MemoryFile 通過將 NAND 或 SD 卡上的文件,分段映射到內(nèi)存中進(jìn)行修改處理几缭,這樣就用高速的 RAM 代替了 ROM 或 SD 卡耗拓,性能自然提高不少,對(duì)于 Android 手機(jī)而言同時(shí)還減少了電量消耗奏司。該類實(shí)現(xiàn)的功能不是很多,直接從 Object 上繼承樟插,通過 JNI 的方式直接在 C 底層執(zhí)行韵洋。</li></ul>

減少 View 的層級(jí)

雖然這或多或少有點(diǎn)渲染優(yōu)化的味道,但是由于 View 也是會(huì)占用一定內(nèi)存的黄锤,所以第一步是通過 Hierarchy Viewer 去去掉多余的 View 層級(jí)搪缨,第二步是通過使用 ViewStub 去對(duì)一些可以延遲加載的 View 做到使用時(shí)加載,一定程度上也可以降低內(nèi)存使用鸵熟。

數(shù)據(jù)相關(guān)

使用 Protocol Buffer 對(duì)數(shù)據(jù)進(jìn)行壓縮(關(guān)于 Protocol Buffer 和其他工具的對(duì)比副编,可以看看這篇文章:thrift-protobuf-compare),Protocol Buffer 相比于 xml 可以減少 30% 的內(nèi)存使用量流强;慎用 SharedPreference痹届,因?yàn)閷?duì)于同一個(gè) SP 有時(shí)候?yàn)榱俗x取一個(gè)字段可能會(huì)將整個(gè) xml 文件都加入內(nèi)存,因此慎用 SP打月,或者可以將一個(gè)大的 SP 分散為幾個(gè)小的 SP队腐;數(shù)據(jù)庫字段盡量精簡(jiǎn),表設(shè)計(jì)合理奏篙,只讀取所需要的字段而不是整個(gè)結(jié)構(gòu)都加載到內(nèi)存當(dāng)中柴淘。

dex 優(yōu)化,代碼優(yōu)化,謹(jǐn)慎使用外部庫

有人覺得代碼多少與內(nèi)存沒有關(guān)系为严,實(shí)際上會(huì)有那么點(diǎn)關(guān)系敛熬,現(xiàn)在稍微大一點(diǎn)的項(xiàng)目動(dòng)輒就是百萬行代碼以上,多 dex 也是常態(tài)第股,不僅占用 Rom 空間应民,實(shí)際上運(yùn)行時(shí)候需要加載的 dex 也是會(huì)占用內(nèi)存的(幾 M),有時(shí)候?yàn)榱耸褂靡恍炖锏哪硞€(gè)功能函數(shù)就引入了整個(gè)龐大的庫是不太合適的炸茧,此時(shí)可以考慮抽取必要部分瑞妇;另外開啟 proguard 優(yōu)化代碼,使用 Facebook redex 優(yōu)化 dex(好像有不少坑)也是一種不錯(cuò)的方式梭冠。

對(duì)象池模式享元模式

對(duì)于對(duì)象的重復(fù)使用來說辕狰,對(duì)象池模式享元模式再合適不過了,具體的可以去看看我博客里面對(duì)于這兩個(gè)模式的介紹和使用控漠。

onLowMemory() 與 onTrimMemory()

我們都知道 Android 用戶可以隨意在不同的應(yīng)用之間進(jìn)行快速切換蔓倍,系統(tǒng)為了讓 Background 的應(yīng)用能夠迅速的切換到 Forground,每一個(gè) Background 的應(yīng)用都會(huì)占用一定的內(nèi)存盐捷。Android 系統(tǒng)會(huì)根據(jù)當(dāng)前的系統(tǒng)的內(nèi)存使用情況偶翅,在一定情況下決定回收部分 Background 的應(yīng)用內(nèi)存,如果 Background 的應(yīng)用從暫停狀態(tài)直接被恢復(fù)到 Forground碉渡,能夠獲得較快的恢復(fù)體驗(yàn)聚谁,如果 Background 應(yīng)用是從 Kill 狀態(tài)進(jìn)行恢復(fù),相比之下就顯得稍微有點(diǎn)慢:

這里寫圖片描述

<ul>
<li>onLowMemory()</li>Android 系統(tǒng)提供了一些回調(diào)來通知當(dāng)前應(yīng)用的內(nèi)存使用情況滞诺,通常來說當(dāng)所有的 Background 應(yīng)用都被 kill 掉的時(shí)候形导,F(xiàn)orground 應(yīng)用會(huì)收到 onLowMemory() 的回調(diào),在這種情況下需要盡快釋放當(dāng)前應(yīng)用的非必須的內(nèi)存資源习霹,從而確保系統(tǒng)能夠繼續(xù)穩(wěn)定運(yùn)行朵耕。
<li>onTrimMemory(int)</li>Android 系統(tǒng)從 4.0 開始還提供了 onTrimMemory() 的回調(diào),當(dāng)系統(tǒng)內(nèi)存達(dá)到某些條件的時(shí)候淋叶,所有正在運(yùn)行的應(yīng)用都會(huì)收到這個(gè)回調(diào)阎曹,同時(shí)在這個(gè)回調(diào)里面會(huì)傳遞指定的參數(shù),代表不同的內(nèi)存使用情況煞檩,收到 onTrimMemory() 回調(diào)的時(shí)候处嫌,需要根據(jù)傳遞的參數(shù)類型進(jìn)行判斷,合理的選擇釋放自身的一些內(nèi)存占用形娇,一方面可以提高系統(tǒng)的整體運(yùn)行流暢度锰霜,另外也可以避免自己被系統(tǒng)判斷為優(yōu)先需要?dú)⒌舻膽?yīng)用,返回的參數(shù):<ul><li> TRIM_MEMORY_BACKGROUND </li><li>TRIM_MEMORY_COMPLETE</li><li>TRIM_MEMORY_MODERATE</li><li>TRIM_MEMORY_RUNNING_CRITICAL</li><li>TRIM_MEMORY_RUNNING_LOW</li><li>TRIM_MEMORY_RUNNING_MODERATE</li><li>TRIM_MEMORY_UI_HIDDEN</li></ul></ul>因?yàn)?onTrimMemory() 的回調(diào)是在 API 14 才被加進(jìn)來的桐早,對(duì)于老的版本癣缅,你可以使用 onLowMemory 回調(diào)來進(jìn)行兼容厨剪,onLowMemory 相當(dāng)與 TRIM_MEMORY_COMPLETE。

謹(jǐn)慎使用多進(jìn)程

使用多進(jìn)程可以把應(yīng)用中的部分組件運(yùn)行在單獨(dú)的進(jìn)程當(dāng)中友存,這樣可以擴(kuò)大應(yīng)用的內(nèi)存占用范圍祷膳,但是這個(gè)技術(shù)必須謹(jǐn)慎使用,絕大多數(shù)應(yīng)用都不應(yīng)該貿(mào)然使用多進(jìn)程屡立,一方面是因?yàn)槭褂枚噙M(jìn)程會(huì)使得代碼邏輯更加復(fù)雜直晨,另外如果使用不當(dāng),它可能反而會(huì)導(dǎo)致顯著的內(nèi)存增加膨俐。當(dāng)你的應(yīng)用需要運(yùn)行一個(gè)常駐后臺(tái)的任務(wù)勇皇,而且這個(gè)任務(wù)并不輕量,可以考慮使用這個(gè)技術(shù)焚刺,一個(gè)典型的例子是創(chuàng)建一個(gè)可以長(zhǎng)時(shí)間后臺(tái)播放的 Music Player敛摘。如果整個(gè)應(yīng)用都運(yùn)行在一個(gè)進(jìn)程中,當(dāng)后臺(tái)播放的時(shí)候乳愉,前臺(tái)的那些 UI 資源也沒有辦法得到釋放兄淫,類似這樣的應(yīng)用可以切分成兩個(gè)進(jìn)程:一個(gè)用來操作 UI,另外一個(gè)給后臺(tái)的 Service蔓姚。

引用

http://blog.csdn.net/luoshengyang/article/details/42555483
http://blog.csdn.net/luoshengyang/article/details/41688319
http://blog.csdn.net/luoshengyang/article/details/42492621
http://blog.csdn.net/luoshengyang/article/details/41338251
http://blog.csdn.net/luoshengyang/article/details/41581063
http://blog.csdn.net/a396901990/article/details/38707007
https://mp.weixin.qq.com/s?__biz=MzA4MzEwOTkyMQ==&mid=2667377215&idx=1&sn=26e3e9ec5f4cf3e7ed1e90a0790cc071&chksm=84f32371b384aa67166a3ff60e3f8ffdfbeed17b4c8b46b538d5a3eec524c9d0bcac33951a1a&scene=0&key=c2240201df732cf062d22d3cf95164740442d817864520af90bb0e71fa51102f2e91475a4f597ec20653c59d305c8a3e518d3f575d419dfcf8fb63a776e0d9fa6d3a9a6a52e84fedf3f467fe4af1ba8b&ascene=0&uin=Mjg5MDI3NjQ2Mg%3D%3D&devicetype=iMac+MacBookPro11%2C4+OSX+OSX+10.12.3+build(16D32)&version=12010310&nettype=WIFI&fontScale=100&pass_ticket=Upl17Ws6QQsmZSia%2F%2B0xkZs9DYxAJBQicqh8rcaxYUjcu3ztlJUPxYrQKML%2BUtuf
http://geek.csdn.net/news/detail/127226
http://www.reibang.com/p/216b03c22bb8
https://zhuanlan.zhihu.com/p/25213586
https://joyrun.github.io/2016/08/08/AndroidMemoryLeak/
http://www.cnblogs.com/larack/p/6071209.html
https://source.android.com/devices/tech/dalvik/gc-debug.html
http://blog.csdn.net/high2011/article/details/53138202
http://gityuan.com/2015/10/03/Android-GC/
http://www.ayqy.net/blog/android-gc-log%E8%A7%A3%E8%AF%BB/
https://developer.android.com/studio/profile/investigate-ram.html
https://zhuanlan.zhihu.com/p/26043999
http://www.csdn.net/article/2015-09-18/2825737

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捕虽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坡脐,更是在濱河造成了極大的恐慌泄私,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件备闲,死亡現(xiàn)場(chǎng)離奇詭異挖滤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浅役,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伶唯,“玉大人觉既,你說我怎么就攤上這事∪樾遥” “怎么了瞪讼?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粹断。 經(jīng)常有香客問我符欠,道長(zhǎng),這世上最難降的妖魔是什么瓶埋? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任希柿,我火速辦了婚禮诊沪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘曾撤。我一直安慰自己端姚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布挤悉。 她就那樣靜靜地躺著渐裸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪装悲。 梳的紋絲不亂的頭發(fā)上昏鹃,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音诀诊,去河邊找鬼洞渤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛畏梆,可吹牛的內(nèi)容都是我干的您宪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奠涌,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宪巨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起溜畅,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤捏卓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慈格,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怠晴,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年浴捆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒜田。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡选泻,死狀恐怖冲粤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情页眯,我是刑警寧澤梯捕,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窝撵,受9級(jí)特大地震影響傀顾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碌奉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一短曾、第九天 我趴在偏房一處隱蔽的房頂上張望寒砖。 院中可真熱鬧,春花似錦错英、人聲如沸入撒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茅逮。三九已至,卻和暖如春判哥,著一層夾襖步出監(jiān)牢的瞬間献雅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工塌计, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挺身,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓锌仅,卻偏偏與公主長(zhǎng)得像章钾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子热芹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容