Android 內存優化
作者:吳小龍同學
原文鏈接:Android 內存優化
為什麼優化
雖然 Java 對內存的釋放有垃圾自動回收機制,但是實際開發中,不再用到的對象因為被錯誤引用會導致無法回收,從而造成內存泄漏,甚至內存溢出 OOM(OutOfMemory),程序崩潰。
Android GC 原理
參考:Android GC 原理探究
Android 內存管理
參考:Android 內存管理
如何檢測
使用 LeakCanary
Android 內存泄漏檢測庫:square/leakcanary ,善於使用 LeakCanary 發現內存泄漏。
Memory Monitor工具
Android Studio 中的 Android Monitor ,選擇其中的 Memory,跟蹤整個 APP 的內存變化情況。
步驟如下:1、與您的應用交互,在 Memory 監視器中,查看 Free 和 Alloated 內存。
2、點擊 Dump Java Heap:,這個會打開 HPROF 查看器,如果沒有,在 Captures 標籤中,雙擊堆快照文件以打開 HPROF 查看器。3、要引起堆分配,請與您的應用交互,然後點擊觸發 GC 的按鈕:Heap Viewer 工具
實時查看App分配的內存大小和空閑內存大小,發現 Memory Leaks 。
詳細使用教程:Heap Viewer工具Allocation Tracker
追蹤內存對象的來源。
詳細使用教程:Allocation Tracker(Device Monitor)優化方案
檢查使用多少內存
每個 APP 的堆(heap)內存大小有硬性限制,如果您的 APP 已達到堆內存限制,並嘗試分配更多的內存,系統會拋出 OutOfMemoryError 。為了避免 OOM ,您可以查詢當前設備有多少堆空間,可以通過調用系統 getMemoryInfo() 查詢,返回 一個ActivityManager.MemoryInfo 對象,它提供該設備當前存儲器的狀態信息,包括可用的存儲器,總存儲器,和低於該閾值存儲器。
private void getMemoryInfo() {n ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);n ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();n activityManager.getMemoryInfo(memoryInfo);n LogUtil.d("totalMem=" + memoryInfo.totalMem + ",availMem=" + memoryInfo.availMem);n if (!memoryInfo.lowMemory) {n // 運行在低內存環境n }n}n
當界面不可見時釋放內存
實現 ComponentCallbacks2 API 中 onTrimMemory()) ,當回調參數 level 為 TRIM_MEMORY_UI_HIDDEN ,是用戶點擊了Home鍵或者Back鍵退出應用,所有UI界面被隱藏,這時候應該釋放一些不可見的時候非必須的資源。
public class MainActivity extends AppCompatActivityn implements ComponentCallbacks2 {n // Other activity code ...n /**n * Release memory when the UI becomes hidden or when system resources become low.n * @param level the memory-related event that was raised.n */n public void onTrimMemory(int level) {n // Determine which lifecycle or system event was raised.n switch (level) {n case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:n /*n Release any UI objects that currently hold memory.n The user interface has moved to the background.n */n break;n case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:n case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:n case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:n /*n Release any memory that your app doesnt need to run.n The device is running low on memory while the app is running.n The event raised indicates the severity of the memory-related event.n If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system willn begin killing background processes.n */n break;n case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:n case ComponentCallbacks2.TRIM_MEMORY_MODERATE:n case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:n /*n Release as much memory as the process can.n The app is on the LRU list and the system is running low on memory.n The event raised indicates where the app sits within the LRU list.n If the event is TRIM_MEMORY_COMPLETE, the process will be one ofn the first to be terminated.n */n break;n default:n /*n Release any non-critical data structures.n The app received an unrecognized memory level valuen from the system. Treat this as a generic low-memory message.n */n break;n }n }n}n
該 onTrimMemory() 回調是在搭載Android 4.0(API級別14)加入。對於早期版本,您可以使用 onLowMemory() 回調作為舊版本的回調,這大致相當於 TRIM_MEMORY_COMPLETE事件。
謹慎使用服務
離開了 APP 還在運行服務是最糟糕的內存管理錯誤之一,當 APP 處在後台,我們應該停止服務,除非它需要運行的任務。我們可以使用 JobScheduler 替代實現,JobScheduler 把一些不是特別緊急的任務放到更合適的時機批量處理。如果必須使用一個服務,最佳方法是使用 IntentService ,限制服務壽命,所有請求處理完成後,IntentService 會自動停止。
使用優化的數據容器
考慮使用優化過數據的容器 SparseArray / SparseBooleanArray / LongSparseArray 代替 HashMap 等傳統數據結構,通用 HashMap 的實現可以說是相當低效的內存,因為它需要為每個映射一個單獨的條目對象。
避免在 Android 上使用枚舉
枚舉往往需要兩倍多的內存,靜態常量更多,我們應該嚴格避免在 Android 上使用枚舉。
使用 nano protobufs 序列化數據
Protocol buffers 是一個語言中立,平台中立的,可擴展的機制,由谷歌進行序列化結構化數據,類似於 XML 設計的,但是更小,更快,更簡單。如果需要為您的數據序列化與協議化,建議使用 nano protobufs。
避免內存流失
內存流失可能會導致出現大量的 GC 事件,如自定義組件的 onDraw() ,避免大量創建臨時對象,比如 String ,以免頻繁觸發 GC。GC 事件通常不影響您的 APP 的性能,然而在很短的時間段,發生許多垃圾收集事件可以快速地吃了您的幀時間,系統上時間的都花費在 GC ,就有很少時間做其他的東西像渲染或音頻流。
使用ProGuard來剔除不需要的代碼
使用 ProGuard 來剔除不需要的代碼,移除任何冗餘的,不必要的,或臃腫的組件,資源或庫完善 APP 的內存消耗。
降低整體尺寸APK
您可以通過減少 APP 的整體規模顯著減少 APP 的內存使用情況。文章:Android APK瘦身實踐
優化布局層次
通過優化視圖層次結構,以減少重疊的 UI 對象的數量來提高性能。文章:Android 渲染優化
使用 Dagger 2依賴注入
依賴注入框架可以簡化您寫的代碼,並提供一個自適應環境測試和便於其他配置的更改。如果打算在您的 APP 使用依賴注入框架,可以考慮用 Dagger 2 ,Dagger 不使用反射掃描 APP 的代碼,Dagger 是靜態的,意味著它編譯時不需要佔用運行 Android 應用或內存的使用。
請小心使用外部庫
外部庫的代碼往往是未針對移動環境下編寫並用於工作在移動客戶端上時,可能是低效的。當您決定使用外部庫,您可能需要優化該庫為移動設備。
避免Bitmap浪費
Bitmap是內存消耗的大頭,當使用時要及時回收。另外配置:
inSampleSize:縮放比例,圖片按需載入,避免不必要的大圖載入。decode format:解碼格式,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差異。Cursor關閉
如查詢資料庫的操作,使用到Cursor,也要對Cursor對象及時關閉。
監聽器的註銷
Android程序裡面存在很多需要register與unregister的監聽器,手動add的listener,需要記得及時remove這個listener。
閱讀原文
推薦閱讀:
※2015年後,如何看待「小米在做加法,魅族在做減法」的說法?
※讓你的電腦變成windows+安卓雙系統
※單手時的可用性對智能手機有多重要?