android性能優化
前言
性能優化本身是一個很大的主題,涵蓋程序的方方麵麵,任何不慎的操作,都有可能對性能造成比較大的影響,要知道程序的性能是可以累加的,多處的性能低下,會影響整體的性能,其後果可能也是多方麵的,本文總結了目前工作中,所需要知道的大部分性能優化點,一部分個人總結,一部分來自於互聯網。但整體上,都是提綱性的,並沒有列出具體的實例,因為寫這方麵主題的達人實在太多了,所以,我得站在巨人的肩膀上,具體細節,請參考對應的鏈接。
性能低下的現象
- 遊戲:界麵很卡,FPS低
- 搜索性能差
- 服務器響應速度慢
- OS:界麵無響應
性能低下的後果:降低用戶體驗
- 用戶流失
- 項目失敗
- 引發災難
- …
容易引發性能問題的點
- 硬件
- IO操作
- 數據庫
- 網絡
- 函數調用
- 數據結構
- 程序邏輯
- ….
從全局考慮
- 硬件性能
- 架構設計
- 核心數據結構
- ……
從微觀考慮
小規模修改程序,提高性能:程序的性能是可以累加的
- 簡單代碼設計
-
類和函數設計:合適的數據類型和算法
- 用快速排序代替冒泡排序
- 用二分查找代替線性查找
- 業務邏輯的實現
- ……
一些觀點
- 高效代碼≠更好代碼≠高質量代碼(易修改,易擴充,易維護)
- 80/20法則:4%的代碼占用了50%以上的運行時間
-
隨時隨地進行優化 ==>將陷入無休止的優化泥潭
- 開發階段前期:功能都未實現,何談優化,但在設計的時候,需要考慮到對應風險。
- 開發階段中後期:功能部分完善,通過現象進行模塊優化
- 開發階段後期:功能完善,充分考慮整體性,通過現象進行係統優化
- 後期優化無法滿足性能要求----架構先天不足,隻能大批量重構
- 過分提高性能會損害程序的可讀性和可維護性
- 優先實現功能,然後再進行優化
- 程序功能的正確性比性能更重要
-
真正高性能的程序設計:
- more small, more fast
- more simple, more fast
- ……
方法論
在進行性能優化前,確保該功能是否已基本完整。
Key Points
- 是否考慮通過修改需求來提高性能?
- 是否考慮通過修改整體設計提高性能?
- 是否考慮通過來修改類的設計提高性能?
- 在開始修改前,程序是完全正確的麼?
- 是否在修改前是否進行性能評估?
- 是否記錄了每次修改後的性能的變化?
- 如果沒有帶來預期的性能提高,是否完全放棄所做的代碼調整?
- 是否對每個性能瓶頸進行不止一次的嚐試?
- 是否反複進行代碼調整,直到最優?
- ……
性能瓶頸的發現
找出瓶頸,集中火力對付占用絕大部分資源的少量代碼。
途徑:
-
Code Review
通過最基本優化策略優化代碼 -
代碼性能測量
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工具
-
Code Review
如何優化
優化本身是一個很大的主題,我這是主要是針對於Android平台來說的。個人認為,優化可以分成好幾部分:
- 一是JAVA語法層次通用的優化,如盡量使用局部變量(棧變量),IO緩衝等。
- 二是通用的Android性能優化,如同步改異步,各種緩存的使用等
- 三是應用程序內部的性能優化,如內部邏輯、數據插入及查找、數據結構的安排與組織等
以下部分針對於上述3種類型,分別進行簡要說明:
基本優化策略:JAVA語法層次的優化
(以下部分來自於:https://blog.csdn.net/aomandeshangxiao/article/details/8115612#t1,具體做法請參考該連接)
-
類和對象使用技巧
- 盡量少用new生成新對象
- 使用clone方法生成新對象
- 盡量使用局部變量棧變量
- 減少方法調用
- 使用final類和final/static/private方法
- 讓訪問實例內變量的 getter/setter 方法變成final
- 避免不需要的 instanceof 操作
- 避免不需要的造型操作
- 盡量重用對象
- 不要重複初始化變量
- 不要過分創建對象
-
Java IO技巧
- 使用緩衝提高IO性能
- lnputStream比Reader高效,OutputStream比Writer高效
- 在適當的時候用byte替代char
- 有緩衝的塊操作IO要比緩衝的流字符IO快
- 序列化時使用原子類型
- 在finally塊中關閉stream
- SQL語句
- 盡早釋放資源
-
異常Exceptions使用技巧
- 避免使用異常來控製程序流程
- 盡可能重用異常
- 將trycatch 塊移出循環
-
線程使用技巧
- 在使用大量線程Threading的場合使用線程池管理
- 防止過多的同步
- 同步方法而不要同步整個代碼段
- 在追求速度的場合用ArrayList和HashMap代替Vector和Hashtable
- 使用notify而不是notifyAll
- 不要在循環中調用 synchronized同步方法
- 單線程應盡量使用 HashMap,ArrayList
-
其它常用技巧
- 使用移位操作替代乘除法操作可以極大地提高性能
- 對Vector中最後位置的添加刪除操作要遠遠快於塒第一個元素的添加刪除操作
- 當複製數組時使用System.arraycop方法
- 使用複合賦值運算符
- 用int而不用其它基本類型
- 在進行數據庫連接和網絡連接時使用連接池
- 用壓縮加快網絡傳輸速度一種常用方法是把相關文件打包到一個jar文件中
- 在數據庫應用程序中使用批處理功能
- 消除循環體中不必要的代碼
- 為vectors 和 hashtables定義初始大小
- 如果隻是查找單個字符的話用charat代替startswith
- 在字符串相加的時候使用 charat()代替startswith() 如果該字符串隻有一個字符的話
- 對於 boolean 值避免不必要的等式判斷
- 對於常量字符串用string 代替 stringbuffer
- 用stringtokenizer 代替 indexof 和substring
- 使用條件操作符替代if cond else 結構
- 不要在循環體中實例化變量
- 確定 stringbuffer的容量
- 不要總是使用取反操作符
- 與一個接口 進行instanceof 操作
- 采用在需要的時候才開始創建的策略
- 通過 StringBuffer 的構造函數來設定他的初始化容量可以明顯提升性能
- 合理使用 javautilVector
- 不要將數組聲明為public static final
- HaspMap 的遍曆
- array數組和 ArrayList 的使用
- StringBufferStringBuilder 的區別
- 盡量使用基本數據類型代替對象
- 用簡單的數值計算代替複雜的函數計算比如查表方式解決三角函數問題
- 使用具體類比使用接口效率高但結構彈性降低了但現代 IDE都可以解決這個問題
- 考慮使用靜態方法
- 應盡可能避免使用內在的GET/SET 方法
- 避免枚舉浮點數的使用
- 二維數組比一維數組占用更多的內存空間大概是 10倍計算
- SQLite
- 奇偶判斷
實際上,Android本身的Training文檔也提供給我們很多可參考的內容,以下僅枚舉一些KeyPoint,當然,有的內容是與上麵的策略是重複的。
原文參考:Performance Tips
其它參考:Android開發性能優化簡介
總體上來說,想要寫出高效代碼,我們要遵循兩條基本的原則:
- 不作沒有必要的工作。
- 盡量避免內存分配。
Key Point:
- 避免創建不必要的對象
- 用靜態代替虛擬
- 避免內部的Getters/Setters
- 對常量使用Static Final修飾符
- 使用改進的For循環語法(for-each)
- 在有內部類的情況考慮使用包權限來替代私有訪問
- 避免使用浮點數
- 了解並使用類庫
- 合理利用Native方法
通用Android性能優化
布局優化
(原文參考:ImprovingLayout Performance)
- 盡量減少Android程序布局中View的層次,View層次越多,效率就越低
- 使用<include/>複用布局
- 使用ViewStub懶加載布局 (TODO:Android布局技巧:使用ViewStub提高UI性能)
- 使用ViewHolder、Thread使ListView滾動更加流暢
其它優化點
- 合理使用異步操作
- 懶加載:當前不需要的數據,不要加載,即按需加載。懶加載的範圍是廣泛的,可以是數據,可以是View,或者其它
-
使用緩存
- 圖片緩存:包括MemoryCache和DiskCache,推薦使用官方DEMO中的Cache
- 單例數據緩存:建立一個管理數據的類,管理所有數據,當主界麵消失後,由於Application本身沒有實際退出,因此,數據本身也沒有釋放掉,下次啟動時,省去了加載數據的時間,當然,這並不是一個好的行為。
- 使用ListView、GridView的View緩存
- 使用Message自身的緩存,避免重複創建Message實例
- 線程池
- 數據池(可參考Message Pool的實現方式)
- ……
-
數據庫優化
- SQL優化
- 建立索引
- 使用事務
- …...
-
算法優化
- 用快速排序代替冒泡排序
- 用二分查找代替線性查找
- ……
-
數據結構使用
- 不要全部使用ArrayList,合理使用LinkedList等易於插入和刪除的集合
- 合理使用HashMap、HashSet來提高查找性能
- 使用SparseArray、SparseIntArray、SparseBooleanArray來替代某些特定的HashMap
- ……
-
其它策略
- 可以考慮延遲處理,避免在同一時間幹過多的事情
應用程序內部的性能優化
該部分的優化應該是依據程序的不同而不同,沒有萬般皆準的法則,目前從我做過的程序來看,實際上,上述的性能優化點基本上已經能夠解決很多性能問題了。
在我所做的程序中,主要的優化手段是:
- 程序邏輯簡化:分析代碼,去掉冗餘邏輯
- 數據結構的優化:對集合類的靈活使用,特別是HashMap的使用,極大的提高查找性能。
- 批量處理原則:對於需要循環調用地方,采用批量處理
總結
性能優化本身是對代碼的重構和反思過程,通過優化性能,能找出我們很多設計、邏輯上的不足。
優化的過程往往很痛苦,但在做過這個過程後,個人在編程水平、設計水平上都會有很大的提高。
很多優化的思想應該做為編碼規範的一部分,需要我們反複實踐,在寫代碼時,第一反應所得出的代碼就是最優的。
最後更新:2017-04-03 12:56:35