本文主要記錄日常開發(fā)中關(guān)于內(nèi)存管理的使用建議,便于查找。大部分來自郭霖的博客,轉(zhuǎn)載地址http://blog.csdn.net/guolin_blog/article/details/42238627。
謹慎使用Service
在使用Service來執(zhí)行后臺任務(wù)時,盡量在需要時才啟動Service;且在任務(wù)完成后,主動調(diào)用stopService或unbindService停止任務(wù)。注意,需要Service停止失敗導(dǎo)致內(nèi)存泄漏的問題。
當(dāng)啟用Service時,安卓系統(tǒng)會傾向于盡量保留Service所在進程,耗費系統(tǒng)內(nèi)存。##Android官方推薦使用IntentService,該組件在后臺任務(wù)執(zhí)行結(jié)束后可以自動停止##,盡量避免了內(nèi)存泄漏的可能。
使用優(yōu)化過的數(shù)據(jù)集合
Android API當(dāng)中提供了一些優(yōu)化過后的數(shù)據(jù)集合工具類,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用這些API可以讓我們的程序更加高效。傳統(tǒng)Java API中提供的HashMap工具類會相對比較低效,因為它需要為每一個鍵值對都提供一個對象入口,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對象數(shù)據(jù)類型的時間。
Bitmap使用
Bitmap對象占用的內(nèi)存空間與像素有直接的關(guān)系,且與圖片在硬盤中的大小無關(guān),建議不要加載不需要的分辨率。以一張100k的圖片為例,像素是15001000,使用ARGB_8888的顏色類型,則每個像素點會占用4個字節(jié)的內(nèi)存,占用的總內(nèi)存是15001000*4字節(jié),即5.7M。
BitmapFactory這個類提供了多個解析方法(decodeByteArray, decodeFile, decodeResource等)用于創(chuàng)建Bitmap對象,我們應(yīng)該根據(jù)圖片的來源選擇合適的方法。比如SD卡中的圖片可以使用decodeFile方法,網(wǎng)絡(luò)上的圖片可以使用decodeStream方法,資源文件中的圖片可以使用decodeResource方法。這些方法會嘗試為已經(jīng)構(gòu)建的bitmap分配內(nèi)存,如果內(nèi)存不足就會出現(xiàn)OOM。為此每一種解析方法都提供了一個可選的BitmapFactory.Options參數(shù),將這個參數(shù)的inJustDecodeBounds屬性設(shè)置為true就可以讓解析方法禁止為bitmap分配內(nèi)存,返回值也不再是一個Bitmap對象,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。這個技巧讓我們可以在加載圖片之前就獲取到圖片的長寬值和MIME類型,從而根據(jù)情況對圖片進行壓縮。
當(dāng)加載多圖時,可以考慮使用內(nèi)存緩存LruCache來快速訪問圖片。該類在android-support-v4包中提供,主要的原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,并且把最近最少使用的對象在緩存值達到預(yù)設(shè)定值之前從內(nèi)存中移除。
目前不建議使用軟引用或弱引用 (SoftReference or WeakReference)來實現(xiàn)內(nèi)存緩存,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的數(shù)據(jù)會存儲在本地的內(nèi)存當(dāng)中,因而無法用一種可預(yù)見的方式將其釋放,這就有潛在的風(fēng)險造成應(yīng)用程序的內(nèi)存溢出并崩潰。
還有一種磁盤緩存DiskLruCache可用來實現(xiàn)圖片的磁盤緩存,解決內(nèi)存緩存空間有限的問題。DiskLruCache是非Google官方編寫,但獲得官方認證的硬盤緩存類,后續(xù)需要學(xué)習(xí)該類的原理和使用方法。
內(nèi)存的開支情況
我們還應(yīng)當(dāng)清楚我們所使用語言的內(nèi)存開支和消耗情況,并且在整個軟件的設(shè)計和開發(fā)當(dāng)中都應(yīng)該將這些信息考慮在內(nèi)。可能有一些看起來無關(guān)痛癢的寫法,結(jié)果卻會導(dǎo)致很大一部分的內(nèi)存開支,例如:
- 使用枚舉通常會比使用靜態(tài)常量要消耗兩倍以上的內(nèi)存,在Android開發(fā)當(dāng)中我們應(yīng)當(dāng)盡可能地不使用枚舉。
- 任何一個Java類,包括內(nèi)部類、匿名類,都要占用大概500字節(jié)的內(nèi)存空間。
- 任何一個類的實例要消耗12-16字節(jié)的內(nèi)存開支,因此頻繁創(chuàng)建實例也是會一定程序上影響內(nèi)存的。eg不要在onDraw中頻繁創(chuàng)建對象
- 在使用HashMap時,即使你只設(shè)置了一個基本數(shù)據(jù)類型的鍵,比如說int,但是也會按照對象的大小來分配內(nèi)存,大概是32字節(jié),而不是4字節(jié)。因此最好的辦法就是像上面所說的一樣,使用優(yōu)化過的數(shù)據(jù)集合。
避免使用依賴注入框架
這類框架一般使用注解實現(xiàn),可以省略findViewById()這一類的繁瑣操作,簡化一些編碼。但是這些框架為了要搜尋代碼中的注解,通常都需要經(jīng)歷較長的初始化過程,并且還可能將一些你用不到的對象也一并加載到內(nèi)存當(dāng)中。這些用不到的對象會一直占用著內(nèi)存空間,可能要過很久之后才會得到釋放,相較之下,也許多敲幾行看似繁瑣的代碼才是更好的選擇。
使用ProGuard簡化代碼
ProGuard相信大家都不會陌生,很多人都會使用這個工具來混淆代碼,但是除了混淆之外,它還具有壓縮和優(yōu)化代碼的功能。ProGuard會對我們的代碼進行檢索,刪除一些無用的代碼,并且會對類、字段、方法等進行重命名,重命名之后的類、字段和方法名都會比原來簡短很多,這樣的話也就對內(nèi)存的占用變得更少了。