記錄一次內(nèi)存優(yōu)化的過程,總結(jié)經(jīng)驗,若有錯誤紕漏歡迎指出。本次優(yōu)化主要分為兩部分:內(nèi)存泄漏問題和內(nèi)存不合理的開銷。本次主要使用的分析工具為?Android Profiler ,具體使用方法文章眾多,不做闡述。
內(nèi)存泄漏
內(nèi)存泄漏可以理解為一個對象存在的生命周期大于實際期望的生命周期。如 Activity 退出后仍被單例對象引用。
案例1:OpenGL 紋理重復(fù)創(chuàng)建
實際問題:項目中一個動畫效果使用 OpenGL 實現(xiàn),每次創(chuàng)建動畫都會調(diào)用?glGenTextures() 重新生成紋理、分配 ID,導(dǎo)致內(nèi)存泄漏。
解決辦法:一次創(chuàng)建好紋理后不再重復(fù)創(chuàng)建。
NOTE:雖然內(nèi)存不會因為動畫創(chuàng)建而持續(xù)增長,但仍有存疑:第一次創(chuàng)建好的紋理是否也無法在頁面退出后正確釋放。
案例2:互相引用
實際問題:Activity 中各種 Manager 與 Activity 相互強引用,Activity 無法釋放導(dǎo)致泄漏。
解決辦法:a)Manager 使用弱引用來引用 Activity。b)可手動管理釋放 Manager 對 Activity 的引用。
案例3:注冊監(jiān)聽后沒有正確釋放
實際問題:在 Activity 中注冊 PhoneStateListener ,Activity 銷毀后沒有移除注冊。典型的匿名內(nèi)部類、非靜態(tài)內(nèi)部類的泄漏問題。
解決辦法:頁面退出時移除注冊 telephonyManager.listen(mOnePhoneStateListener, PhoneStateListener.LISTEN_NONE);?
不合理開銷
案例1:視圖懶加載
實際問題:引導(dǎo)蒙層、錯誤視圖等都使用了尺寸較大的圖片,而引導(dǎo)蒙層用戶只有第一次使用功能時需要展示,錯誤視圖如在成功加載數(shù)據(jù)后不會有機會展示。?
解決辦法:使用 ViewStub 懶加載,而非 VISIBLE、GONE 控制展示與否。
案例2:減少不必要的 Bitmap 開銷
設(shè)計可能會給出組合或?qū)盈B兩張或多張圖片的 UI 效果。這些 Bitmap 都是占內(nèi)存的,盡量使用一張圖搞定。
資源文件只打包了xxhdpi的,較大的圖片在適配不同大小的控件、屏幕時使用 Glide 加載,優(yōu)化圖片的內(nèi)存占用。
案例3:默認(rèn)圖、占位圖的釋放
在請求服務(wù)端圖片資源成功前、高斯模糊完成展示前,通常會先使用一張圖片去做默認(rèn)的展示,在上訴操作完成并展示后釋放掉不再需要的默認(rèn)圖。
上訴為實際中碰到的案例,總結(jié)問題模式:
1)OpenGL 不要重復(fù)創(chuàng)建紋理。
2)其他類持有 Context 對象 (非 ApplicationContext)請使用弱引用,否則需要手動釋放對?Context 的引用。
3)注意匿名內(nèi)部類、非靜態(tài)內(nèi)部類潛在的泄漏模式。
4)視圖懶加載,使用 ViewStub 在需要的時候加載視圖。
5)不要使用層疊 Bitmap 實現(xiàn) UI 效果,會多出一倍的內(nèi)存開銷。
6)使用 Glide 優(yōu)化圖片加載的內(nèi)存使用。
7)較大的默認(rèn)圖、占位圖手動釋放。