Android APP 記憶體優化之圖片優化

發表於2016-07-13

網上有很多大拿分享的關於Android效能優化的文章,主要是通過各種工具分析,使用合理的技巧優化APP的體驗,提升APP的流暢度,但關於記憶體優化的文章很少有看到。在Android裝置記憶體動不動就上G的情況下,的確沒有必要去太在意APP對Android系統記憶體的消耗,但在實際工作中我做的是教育類的小學APP,APP中的按鈕、背景、動畫變換基本上全是圖片,在2K屏上(解析度2048*1536)一張背景圖片就會佔用記憶體12M,來回切換幾次記憶體佔用就會增漲到上百兆,為了在不影響APP的視覺效果的前提下,有必要通過各種手段來降低APP對記憶體的消耗,下面是我在實踐過程中使用的一些方法,很多都是不太成熟的專案,也不夠深入,只是將其作為一種處理方式分享給大家。

通過DDMS的APP記憶體佔用檢視工具分析發現,APP中佔用記憶體最多的是圖片,每個Activity中圖片佔用記憶體佔大半,本文重點分享對圖片的記憶體優化。

不要將Button的背景設定為selector

在佈局檔案和程式碼中,都可以為Button設定background為selector,這樣方便實現按鈕的正反選效果,但實際跟蹤發現,如果是將Button的背景設定為selector,在初始化Button的時候會將正反選圖片都載入在記憶體中(具體可以檢視Android原始碼,在類Drawable.java的createFromXmlInner方法中對圖片進行解析,最終呼叫Drawable的inflate方法),相當於一個按鈕佔用了兩張相同大小圖片所使用的記憶體,如果一個介面上按鈕很多或者是按鈕很大,光是按鈕佔用的記憶體就會很大,可以通過在佈局檔案中給按鈕只設定正常狀態下的背景圖片,然後在程式碼中監聽按鈕的點選狀態,當按下按鈕時為按鈕設定反選效果的圖片,抬起時重新設定為正常狀態下的背景,具體實現方式如下:

通過上面這種方式就可以解決同一個按鈕佔用兩倍記憶體的問題,如果你覺得為一個按鈕提供正反選兩張圖片會導致APK的體積變大,可以通過如下方式實現按鈕點選的反選效果,這種方式既不會存在Button佔用兩倍記憶體的情況,又減小了APK的體積(Android 5.0中的tintColor也可以實現類似的效果):

將背景圖片放在非UI執行緒繪製,提升APP的效率

在高解析度的平板裝置上,繪製大背景的圖片會影響程式的執行效率,嚴重情況下就和沒有開硬體加速的時候使用手寫功能一樣,相當地卡,最後我們的解決方案是將背景圖片通過SurfaceView來繪製,這樣相當於是在非UI執行緒繪製,不會影響到UI執行緒做其它事情:

在res/values/attr.xml檔案中定義自定義View的自定義屬性:

沒有必要使用硬體加速的介面建議關掉硬體加速

通過DDMS的heap跟蹤發現,相比於關閉硬體加速,在開啟硬體加速的情況下會消耗更多的記憶體,但有的介面開啟或者關閉硬體加速對程式的執行效率並沒有太大的影響,此種情況下可以考慮在AndroidManifest.xml檔案中關閉掉對應Activity的硬體加速,like this:

注意:如果使用到WebView、視訊播放、手寫、動畫等功能時,關掉硬體加速會嚴重音效程式的執行效率,這種情況可以只關閉掉Activity中某些view的硬體加速,整個Activity的硬體加速不關閉。

如果Activity中某個View需要關閉硬體加速,但整個Activity不能關閉,可以呼叫view層級關閉硬體加速的方法:

儘量少用AnimationDrawable,如果必須要可以自定義圖片切換器代替AnimationDrawable

AnimationDrawable也是一個耗記憶體大戶,圖片幀數越多耗記憶體越大,具體可以檢視AnimationDrawable的原始碼,在AnimationDrawable例項化的時候,Drawable的createFromXmlInner方法會呼叫AnimationDrawable的inflate方法,該方法裡面有一個while迴圈去一次性將所有幀都讀取出來,也就是在初始化的時候就將所有的幀讀在記憶體中了,有多少張圖片,它就要消耗對應大小的記憶體。

雖然可以通過如下方式釋放AnimationDrawable佔用的記憶體,但是當退出使用AnimationDrawable的介面,再次進入使用其播放動畫時,會報使用已經回收了的圖片的異常,這個應該是Android對圖片的處理機制導致的,雖然Activity被finish掉了,但是這個Activity中使用到的圖片還是在記憶體中,如果被回收,下次進入時就會報異常資訊:

通常情況下我會自定義一個ImageView來實現AnimationDrawable的功能,根據圖片之間切換的時間間隔來定時設定ImageView的背景圖片,這樣始終只是一個ImageView例項,更換的只是其背景,佔用記憶體會比AnimationDrawable小很多:

其它優化方式

  • 儘量將Activity中的小圖片和背景合併,一張小圖片既浪費佈局的時間,又平白地增加了記憶體佔用;
  • 不要在Activity的主題中為Activity設定預設的背景圖片,這樣會導致Activity佔用的記憶體翻倍:<!–千萬不要在主題中為Activity設定預設背景<style name=”Activity_Style” parent=”@android:Theme.Holo.Light.NoActionBar”>
    <item name=”android:background”>@drawable/*</item>
    </style>
  • 對於在需要時才顯示的圖片或者佈局,可以使用ViewStub標籤,通過sdk/tools目錄下的hierarchyviewer.bat檢視佈局檔案會發現,使用viewstub標籤的元件幾乎不消耗佈局的時間,在程式碼中當需要顯示時再去例項化有助於提高Activity的佈局效率和節省Activity消耗的記憶體。

相關文章