Android效能優化總結

lostinai發表於2014-05-05

前言

效能優化本身是一個很大的主題,涵蓋程式的方方面面,任何不慎的操作,都有可能對效能造成比較大的影響,要知道程式的效能是可以累加的,多處的效能低下,會影響整體的效能,其後果可能也是多方面的,本文總結了目前工作中,所需要知道的大部分效能優化點,一部分個人總結,一部分來自於網際網路。但整體上,都是提綱性的,並沒有列出具體的例項,因為寫這方面主題的達人實在太多了,所以,我得站在巨人的肩膀上,具體細節,請參考對應的連結。

效能低下的現象

  • 遊戲:介面很卡,FPS低
  • 搜尋效能差
  • 伺服器響應速度慢
  • OS:介面無響應

效能低下的後果:降低使用者體驗

  • 使用者流失
  • 專案失敗
  • 引發災難

容易引發效能問題的點

  • 硬體
  • IO操作
  • 資料庫
  • 網路
  • 函式呼叫
  • 資料結構
  • 程式邏輯
  • ….

從全域性考慮

  • 硬體效能
  • 架構設計
  • 核心資料結構
  • ……

從微觀考慮

小規模修改程式,提高效能:程式的效能是可以累加的

  • 簡單程式碼設計
  • 類和函式設計:合適的資料型別和演算法
    • 用快速排序代替氣泡排序
    • 用二分查詢代替線性查詢
  • 業務邏輯的實現
  • ……

一些觀點

  • 高效程式碼更好程式碼高質量程式碼(易修改,易擴充,易維護)
  • 80/20法則:4%的程式碼佔用了50%以上的執行時間
  • 隨時隨地進行優化 ==>將陷入無休止的優化泥潭
    • 開發階段前期:功能都未實現,何談優化,但在設計的時候,需要考慮到對應風險。
    • 開發階段中後期:功能部分完善,通過現象進行模組優化
    • 開發階段後期:功能完善,充分考慮整體性,通過現象進行系統優化
    • 後期優化無法滿足效能要求----架構先天不足,只能大批量重構
  • 過分提高效能會損害程式的可讀性和可維護性
  • 優先實現功能,然後再進行優化
  • 程式功能的正確性比效能更重要
  • 真正高效能的程式設計:
    • more small, more fast
    • more simple, more fast
  • ……

方法論

在進行效能優化前,確保該功能是否已基本完整。

 


Key Points

  • 是否考慮通過修改需求來提高效能?
  • 是否考慮通過修改整體設計提高效能?
  • 是否考慮通過來修改類的設計提高效能?
  • 在開始修改前,程式是完全正確的麼?
  • 是否在修改前是否進行效能評估?
  • 是否記錄了每次修改後的效能的變化?
  • 如果沒有帶來預期的效能提高,是否完全放棄所做的程式碼調整?
  • 是否對每個效能瓶頸進行不止一次的嘗試?
  • 是否反覆進行程式碼調整,直到最優?
  • ……

效能瓶頸的發現

找出瓶頸,集中火力對付佔用絕大部分資源的少量程式碼。

途徑:

    1. Code Review
      通過最基本優化策略優化程式碼
    2. 程式碼效能測量
      1) 通過效能Log記錄函式呼叫時間,找出瓶頸點
          LogUtil.d(TAGConstant.TAG_PERFORMANCE,"Load media info into group begin......");
          longstart = System.currentTimeMillis();
                       ……
          LogUtil.d(TAGConstant.TAG_PERFORMANCE,“Load media info into group end, Total Time: ” + (System.currentTimeMillis() -start) + “ ms”);
      2) 使用效能分析工具:
          Traceview(參考:Android效能調優工具TraceView介紹
          Monkey
          monkeyrunner
           注:後兩個我個人沒有使用過,不做介紹,優先推薦使用TraceView工具 

如何優化

優化本身是一個很大的主題,我這是主要是針對於Android平臺來說的。個人認為,優化可以分成好幾部分:

    1. 一是JAVA語法層次通用的優化,如儘量使用區域性變數(棧變數),IO緩衝等。
    2. 二是通用的Android效能優化,如同步改非同步,各種快取的使用等
    3. 三是應用程式內部的效能優化,如內部邏輯、資料插入及查詢、資料結構的安排與組織等

 

以下部分針對於上述3種型別,分別進行簡要說明:

基本優化策略:JAVA語法層次的優化

(以下部分來自於:http://blog.csdn.net/aomandeshangxiao/article/details/8115612#t1,具體做法請參考該連線)

  1. 類和物件使用技巧
    1. 儘量少用new生成新物件
    2. 使用clone方法生成新物件
    3. 儘量使用區域性變數棧變數
    4. 減少方法呼叫
    5. 使用final類和final/static/private方法
    6. 讓訪問例項內變數的 getter/setter 方法變成final  
    7. 避免不需要的 instanceof 操作  
    8. 避免不需要的造型操作  
    9. 儘量重用物件  
    10. 不要重複初始化變數  
    11. 不要過分建立物件
  1. Java IO技巧
    1. 使用緩衝提高IO效能
    2. lnputStream比Reader高效,OutputStream比Writer高效
    3. 在適當的時候用byte替代char
    4. 有緩衝的塊操作IO要比緩衝的流字元IO
    5. 序列化時使用原子型別
    6. 在finally塊中關閉stream 
    7. SQL語句
    8. 儘早釋放資源
  1. 異常Exceptions使用技巧
    1. 避免使用異常來控制程式流程
    2. 儘可能重用異常
    3. 將trycatch 塊移出迴圈  
  1. 執行緒使用技巧
    1. 在使用大量執行緒Threading的場合使用執行緒池管理
    2. 防止過多的同步
    3. 同步方法而不要同步整個程式碼段
    4. 在追求速度的場合用ArrayList和HashMap代替Vector和Hashtable
    5. 使用notify而不是notifyAll
    6. 不要在迴圈中呼叫 synchronized同步方法   
    7. 單執行緒應儘量使用 HashMap,ArrayList
  1. 其它常用技巧
    1. 使用移位操作替代乘除法操作可以極大地提高效能
    2. 對Vector中最後位置的新增刪除操作要遠遠快於塒第一個元素的新增刪除操作
    3. 當複製陣列時使用System.arraycop方法
    4. 使用複合賦值運算子
    5. 用int而不用其它基本型別
    6. 在進行資料庫連線和網路連線時使用連線池
    7. 用壓縮加快網路傳輸速度一種常用方法是把相關檔案打包到一個jar檔案中
    8. 在資料庫應用程式中使用批處理功能
    9. 消除迴圈體中不必要的程式碼
    10. 為vectors 和 hashtables定義初始大小  
    11. 如果只是查詢單個字元的話用charat代替startswith
    12. 在字串相加的時候使用 charat()代替startswith() 如果該字串只有一個字元的話  
    13. 對於 boolean 值避免不必要的等式判斷  
    14. 對於常量字串用string 代替 stringbuffer   
    15. 用stringtokenizer 代替 indexof 和substring  
    16. 使用條件操作符替代if cond else  結構 
    17. 不要在迴圈體中例項化變數  
    18. 確定 stringbuffer的容量  
    19. 不要總是使用取反操作符  
    20. 與一個介面 進行instanceof 操作  
    21. 採用在需要的時候才開始建立的策略  
    22. 通過 StringBuffer 的建構函式來設定他的初始化容量可以明顯提升效能  
    23. 合理使用 javautilVector
    24. 不要將陣列宣告為public static final
    25. HaspMap 的遍歷
    26. array陣列和 ArrayList 的使用  
    27. StringBufferStringBuilder 的區別
    28. 儘量使用基本資料型別代替物件   
    29. 用簡單的數值計算代替複雜的函式計算比如查表方式解決三角函式問題  
    30. 使用具體類比使用介面效率高但結構彈性降低了但現代 IDE都可以解決這個問題 
    31. 考慮使用靜態方法
    32. 應儘可能避免使用內在的GET/SET 方法 
    33. 避免列舉浮點數的使用   
    34. 二維陣列比一維陣列佔用更多的記憶體空間大概是 10倍計算 
    35. SQLite
    1. 奇偶判斷

 

實際上,Android本身的Training文件也提供給我們很多可參考的內容,以下僅列舉一些KeyPoint,當然,有的內容是與上面的策略是重複的。

原文參考:Performance Tips

譯文參考:Android應用開發者指南:效能優化(1)

其它參考:Android開發效能優化簡介

總體上來說,想要寫出高效程式碼,我們要遵循兩條基本的原則:

  • 不作沒有必要的工作。
  • 儘量避免記憶體分配。

Key Point

  • 避免建立不必要的物件
  • 用靜態代替虛擬
  • 避免內部的Getters/Setters
  • 對常量使用Static Final修飾符
  • 使用改進的For迴圈語法(for-each
  • 在有內部類的情況考慮使用包許可權來替代私有訪問
  • 避免使用浮點數
  • 瞭解並使用類庫
  • 合理利用Native方法

 

通用Android效能優化

佈局優化

(原文參考:ImprovingLayout Performance

  • 儘量減少Android程式佈局中View的層次,View層次越多,效率就越低

 

其它優化點

  • 合理使用非同步操作
  • 懶載入:當前不需要的資料,不要載入,即按需載入。懶載入的範圍是廣泛的,可以是資料,可以是View,或者其它
  • 使用快取
    • 圖片快取:包括MemoryCacheDiskCache,推薦使用官方DEMO中的Cache

參考:DisplayingBitmaps Efficiently

  • 單例資料快取:建立一個管理資料的類,管理所有資料,當主介面消失後,由於Application本身沒有實際退出,因此,資料本身也沒有釋放掉,下次啟動時,省去了載入資料的時間,當然,這並不是一個好的行為。
  • 使用ListViewGridViewView快取
  • 使用Message自身的快取,避免重複建立Message例項
  • 執行緒池
  • 資料池(可參考Message Pool的實現方式)
  • ……
  • 資料庫優化
    • SQL優化
    • 建立索引
    • 使用事務
    • …...
  • 演算法優化
    • 用快速排序代替氣泡排序
    • 用二分查詢代替線性查詢
    • ……
  • 資料結構使用
    • 不要全部使用ArrayList,合理使用LinkedList等易於插入和刪除的集合
    • 合理使用HashMapHashSet來提高查詢效能
    • 使用SparseArray、SparseIntArraySparseBooleanArray來替代某些特定的HashMap
    • ……
  • 其它策略
    • 可以考慮延遲處理,避免在同一時間幹過多的事情

應用程式內部的效能優化

該部分的優化應該是依據程式的不同而不同,沒有萬般皆準的法則,目前從我做過的程式來看,實際上,上述的效能優化點基本上已經能夠解決很多效能問題了。

在我所做的程式中,主要的優化手段是:

  • 程式邏輯簡化:分析程式碼,去掉冗餘邏輯
  • 資料結構的優化:對集合類的靈活使用,特別是HashMap的使用,極大的提高查詢效能。
  • 批量處理原則:對於需要迴圈呼叫地方,採用批量處理

總結

效能優化本身是對程式碼的重構和反思過程,通過優化效能,能找出我們很多設計、邏輯上的不足。

優化的過程往往很痛苦,但在做過這個過程後,個人在程式設計水平、設計水平上都會有很大的提高。

很多優化的思想應該做為編碼規範的一部分,需要我們反覆實踐,在寫程式碼時,第一反應所得出的程式碼就是最優的。

相關文章