1. 程式人生 > >Android優化相關總結

Android優化相關總結

記憶體優化

記憶體優化的幾種方法:

  1. 需要使用多少記憶體申請多少記憶體。

    • 比如例項化陣列大小。
    • 建立Map時如果Key值支存在Int型別的話可以嘗試使用SparseArray。
    • 載入圖片的時候,在不需要特別高畫質的情況下可以採用壓縮或者使用佔用記憶體較小的圖片格式,如預設的圖片格式是ARGB_8888可以換成RGB_565。
  2. 物件不再使用之後及時進行回收與關閉。比如Bitmap,一般Bitmap佔用的記憶體都會比較大,雖然ImageView在回收後會自動釋放Bitmap,但是這樣可能存在一定時間的延後,大量的記憶體需要進行GC也可能導致系統停頓。另外需要注意的是Android3.0之前Bitmap物件的畫素資料是存放在Native記憶體中的,所以需要手動呼叫recycler()進行記憶體回收,Android3.0之後記憶體全部存放在了Dalvik Heap中,這時就可以由虛擬機器進行記憶體管理了。

  3. 預防記憶體洩漏的發生,出現記憶體洩漏之後會導致申請的記憶體不能夠被虛擬機器回收,最後出現OOM的問題。

  4. 防止靜態物件持有其它物件的引用,特別是靜態的List或者Map物件。

  5. 廣播或者其它第三方庫在不需要使用後要記得進行反註冊或者解綁。

比較常見的第三方庫比如EventBus、Rxjava之類。

  1. 提高物件的複用利用率:

    1. Bitmap物件可以設定BitmapFactory.Options.inBitmap,Android4.4後只要被複用的Bitmap大於需要新申請記憶體的Bitmap即可。
    2. 減少方法中臨時變數的建立。舉個例子,如果在View的onDraw方法中初始化新的物件那麼就將代表著每隔16ms都會例項化一個這個物件出來,因為正常情況下每隔16ms將會進行一個View的重新繪製。
  2. 清單檔案中使用largeHeap="true"屬性來開啟大記憶體

出現記憶體洩漏的原因與解決方法

  • Handler出現記憶體洩漏原因:
    1. Handler初始化使用的是匿名內部類的方式。
    2. Handler的初始化使用的是非靜態內部類。

這幾種方式都會隱式的含有一個父物件的引用,如果是在Activity中建立,就會導致Activity一直不能夠回收。

  • Handler記憶體洩漏的解決方法:

    1. 使用靜態的內部類建立Handler例項。
    2. 在Activity等元件中可以使用WeakReference引用相應的物件,這樣不會影響GC的正常執行。
  • 單例模式導致的記憶體洩漏

我們在建立單例的靜態物件的時候經常會需要一個Context物件,特別常見的是傳入的是一個Activity物件,由於被靜態物件強引用,即使呼叫了Activity的finish()方法,Activity物件還是一直沒法被虛擬機器正常回收。

解決的方法也很簡單,我們可以傳入一個Applicaton替換傳入的其它需要被及時回收的Context物件。

  • 使用屬性動畫也可能導致記憶體洩漏的發生

屬性動畫的記憶體洩漏很像Handler的記憶體洩漏,屬性動畫在未執行完成前會持有相應執行動畫View的引用,此時即使進行finish()可能就會出現Activity無法回收的情況發生。

解決的方法呢就是在Activity的onDestory()方法中執行屬性動畫的cancel()方法,釋放引用的View物件。

  • 靜態的Map和List導致的記憶體洩漏

及時的移除不再使用的集合物件。

相關的開源庫與工具

  • LeakCanary

  • MAT

ANR出現的原因與調查方法

ANR之後會將相關的anr檔案儲存到data/anr/traces.txt下。

相關的開源庫與工具

  • Hugo

通過@DebugLog註解,可以使用在Class或者Method域上,通過Aspectj進行程式碼注入,可以打印出輸入與輸出和Method執行的時間。

  • BlockCanary

這種進行UI主執行緒耗時的監聽方式真的很巧妙,不得不佩服作者思維的靈活。

熟悉Looper原始碼的可以知道,Looper的loop()方法在呼叫dispatchMessage()前後會分別呼叫通過 Looper.getMainLooper().setMessageLogging()傳入的Print物件的println()方法,這時我們就能夠利用這個方法統計dispatchMessage()執行消耗的事件,與其它很多有用的資訊。

原理:

  • Android Profiler

佈局優化

當一個專案的介面變得越來越複雜之後,介面佈局的層級可能會變得非常的多,這時介面的measure、layout和繪製就可能會非常的耗時。因此我們可以從佈局層級來進行優化。

  • layout標籤

  • ViewStub標籤

  • Merge標籤:

  • ConstraintLayout:

  • RecyclerView的Adapter中ViewHolder可以使用純程式碼替代xml檔案提高建立速度

相關的開源庫與工具

  • HierarchyViewer (新版本已經被移除)
  • Layout inspector

介面啟動優化

  • Application的onCreate()方法中減少方法呼叫,第三方庫的初始化需要按照優先級別進行分類,非必須的進行延後載入。
  • 介面的初始化例如Fragment的懶載入setUserVisibleHint()
  • 修改啟動時的Theme,提高使用者體驗
  • 介面的繪製儘量採用懶載入的機制,減少啟動介面時介面繪製消耗的時間

APK大小的優化

  • shrinkresouce
  • .so檔案過濾掉不常見的cpu架構,如:x86
  • 優化圖片的格式,在對圖片質量要求不高的情況下,使用有失真壓縮的格式。
  • 使用svg形式的圖片顯示
  • proguard 進行程式碼混淆

網路請求優化

網路請求優化的點主要在請求速度與流量消耗兩方面:

  • 壓縮通訊時的JSON文字,可以採用GZIP壓縮傳輸的資料。

  • 介面設計時攜帶更多有用資訊,刪除無用的欄位,優化業務邏輯,合併介面。

  • 圖片請求使用合適的快取策略:

    1. 記憶體快取LruCache。
    2. 磁碟快取DiskLruCache。
  • 設計合適的快取策略,思考哪些內容可能短時間不會發生改變,或者即使發生了變化使用者從體感上也不會有所察覺,比如說

  • 使用GSON流式解析。

電量優化

  1. 推送與通知除非是非常有必要的情況下,比如說“收到錢”這類的重要事情,其它情況下儘量不要喚醒應用。
  2. 減少網路請求的次數。
  3. 如果需要定位時,如果不需要特別精確的座標的話,可以使用Wifi或者行動網路進行定位。

例如: 電臺應用可能是需要判斷當前是哪個城市即可,然後根據城市判斷顯示哪些頻道可以顯示出來。