天天看點

Android開發中的性能優化---代碼

  這篇主要總結一些優化代碼的技巧,一些寫代碼中的小細節,可能就會影響程式的執行效率!!! 

  主要遵循兩個原則 

  1.不要建立一些沒必要建立的對象以及重複定義某個變量 

  對象的建立是一個非常繁瑣的步驟,JVM首先會對通過

new

指令對符号進行解析,以此來判斷該類是否被加載,然後在堆中進行記憶體配置設定,為對象配置設定完記憶體空間後,就會對記憶體區域進行初始化(該為0的為0,該為null的為null,這也是為什麼方法中的變量需要程式員顯示初始化),最後JVM會調用對象的構造函數,而且一般都會有一個變量建立該對象的引用。 

  是以,建立對象是很麻煩的,就不要沒事幹就建立對象了。 

   

  2.用完的對象要及時回收 

  就是常說的GC,這裡要明确的是,什麼時候進行GC操作,不是由程式員決定的,JVM會在有必要的時候自行啟動GC,程式員隻能做到建議JVM盡快對某對象進行GC,它有兩個評判标準 

  (1) 該對象失去引用,那麼最好的辦法就是将不用的對象指派為null 

  (2) 該對象離開作用域,比如說方法執行完畢,局部變量和對象就會被回收,是以盡可能少生命全局變量。 

  并且GC操作也是要耗記憶體的@_@! 

  

開始

1.最頻繁的 + 和 +“” 操作

  Java中的 +号既可以進行數值計算,又可以進行字元串操作,但是在進行字元串操作的時候,它是一種效率很低的方式,雖然用起來非常非常友善!

1.1 不要直接使用 +号 進行字元串拼接

  開發中使用“+”号運算符的确非常友善,但是效率也非常低,因為“+”号内部實際上調用的是

StringBuffer

append()

方法,然後再轉換成

String

,那麼它肯定沒有直接調用快。 

  還有一個就是

StringBuilder

,這個類是Java1.5時出來的,從效率上來說,它和

StringBuffer

差別在于

StringBuilder

不是線程安全的,多線程操作會出現問題,那麼就比較容易選擇了, 

  在單線程中,使用

StringBuilder

,在多線程中使用

StringBuffer

1.2 不要直接使用 +“” 來進行字元串轉化

  将對象轉化成字元串的操作有三種,效率從快到慢依次是,

xxx.toString

String.valueOf(xxx)

、 

xxx +""

xxx.toString

是直接轉化,而

String.valueOf(xxx)

底層是調用了

toString()

方法,至于

+""

,在1.1時已經說了,它會先轉化成

SringBuffer

,然後再調用

toString()

,是以結果一目了然

上述兩點可以看下面的代碼

Android開發中的性能優化---代碼

通過javap -c 的指令, 能夠看到下面的内容, 雖然左邊的不一定能看懂, 但是右邊的注釋很容易了解

Android開發中的性能優化---代碼

2.循環的使用細節(以for為例)

  循環是開發中頻率出現很高的一種操作,它的作用就是簡化重複的步驟,比如說添加10000條資料到某個集合中,如果不用循環,即使複制粘貼也要好久,但是通過循環操作就可以幾行代碼完成。但這裡面就會容易出現一些小的細節,有可能就會影響程式的執行效率。以下代碼舉例:

1 List<String> list = new ArrayList<>();
2 for (int i = 0; i < 100; i++) {
3     Random random = new Random();
4     Integer result = random.nextInt(100);
5     String s = result.toString();
6     list.add(s);
7 }          

2.1 盡量不要在循環中建立不必要的對象

  示例代碼中在for循環中建立了100個Random對象,而實際進行随機數操作的是該對象的

nextInt()

方法,那麼實際上可以寫成:

List<String> list = new ArrayList<>();
// 對象建立在for循環外
Random random = new Random();
for (int i = 0; i < 100; i++) {
    Integer result = random.nextInt(100);
    String s = result.toString();
    list.add(s);
}      

2.2 不用的對象要及時回收

  這裡隻是舉個例子,實際開發中有可能出現的情況是,解析一段Json,然後擷取到了Json中的某個對象并添加到了集合中,那麼這個被解析過的對象其實就沒什麼用了,因為資料已經添加到了集合中,比較我們要用的是資料而不是對象,此時就可以置為null,讓JVM盡快的回收掉它,這是一個非常好的習慣,

  但有些時候例外, 面這種情況, 将對象置為null, 但實際上該對象的引用仍然被list集合所持有, 是以對象不會被回收掉

1 List<String> list = new ArrayList<>();
 2 Random random = new Random();
 3 Integer result;
 4 String s;
 5 for (int i = 0; i < 100; i++) {
 6     result = random.nextInt(100);
 7     s = result.toString();
 8     list.add(s);
 9     result = null;  // 此時雖然将對象置為null, 但是它的引用依然是被list所持有的, 即使result置為null, 仍然不會來回收
10     s = null;
11 }      

  比較好的做法應該是清空list集合然後将list置為null

1 list.clear();  // 要先将list所持有的對象給清空掉
2 if(list != null) {
3     list = null;
4 }      

在實際開發中, 可以寫在onDestory()方法中

2.4 适當的建立方法結果的緩存

  比如有時候為了簡便會這樣寫 

  Java在調用某個方法時,其實也不是那麼簡單,要建立棧幀來存儲局部變量表,操作數棧,動态連接配接,方法傳回位址,還要方法調用現場、恢複方法調用現場等一系列操作。是以,如果list的size()非常大時,也會影響效率,是以可以定義一個變量而不是直接使用 

3. if和switch的使用

  既然有了循環,那麼就應當有判斷@_@!

3.1 通過if來在合适的時候建立對象或調用方法

  這個也算一個小細節 

  這個循環裡面可以看出,隻有在随機數為20的時候集合才會添加該資料,是以在此之前,将随機數調用

toString()

方法毫無意義。是以可以寫為 

4 使用位運算來代替乘除運算

  計算機隻懂二進制,而位運算就是直接的二進制運算,是以當然效率比較高,

  當然也有缺點 

    (1) 對于數字過大,還是直接乘除比較友善 

    (2) 代碼的可讀性不高

5 合理利用Activity的生命周期

  Activity的生命周期最常用的莫過于

onCreate()

,但是其它的生命周期我想也很有必要恰當的利用。 

  這個僅僅是個人書寫習慣,不知道會不會影響到性能,但是最起碼這樣寫代碼結構比較清晰 :)

6 使用Zipalign來進行代碼對齊

  Zipalign是一個SDK下的工具,能夠對apk檔案中未壓縮的資料在4個位元組邊界上對齊,當資源檔案通過記憶體映射對齊到4位元組邊界時,android系統就可以通過調用mmap函數讀取檔案, 

  mmap函數是Linux系統的調用函數,提供了不同于一般對普通檔案的通路方式,程序可以像讀寫記憶體一樣對普通檔案的操作,程序可以像讀寫記憶體一樣對普通檔案的操作,不必再read()和write(),在讀取資源上獲得較高的性能。如果資源本身沒有進行對齊處理,它就必須顯式地讀取它們——這個過程将會比較緩慢且會花費額外的記憶體。 

  具體用法我會在工具篇中總結

7 使用SparseArray代替HashMap  使用

SparseArray

來代替

HashMap

,原因是一次Android Studio的提示, 

  使用

SparseArray

HashMap

好,是以就網上查找了些

SparseArray

的一些相關資料 

  簡單的來講,

SparseArray

适用于

HashMap<Integer, Object>

這樣的結構,因為

SparseArray

内部是用的

int

型數組存儲

key

Object

型的數組存儲

value

的值,省去了

int

包裝成

Integer

的過程. 

  根據這篇文章,可以看出來,在資料過多時,效率上

SparseArray

并沒有比

HashMap

更快,但是該文章作者結尾處說記憶體的使用狀況上,能夠節約27% 

  而這篇文章的作者專門對記憶體的使用狀況做了測試,說是記憶體上優化了35% 

  

  我通過Android Studio自帶的工具觀察記憶體消耗,使用

HashMap

大約在3.24~3.31 

  而

SparseArray

大約在3.0~3.1之間 

    不過具體優化了多少不是重點,反正是對記憶體開銷上

SparseArray開銷更低

更具有優勢~! 

 另外除了

SparseArray

,

ArrayMap

,SparseBoolMap,SparseIntMap,SparseLongMap,LongSparseMap等等一些列API,在記憶體使用方面都相對于HashMap要好的多

8 I/O流盡量使用帶Buffer的

  帶Buffer緩沖區的I/O對象效率要比不帶的高一些,

9. 能不static就不static,能final就final

static

雖然讓成員用起來很爽,但是會延長它們的生命周期 

final

會使對象無法被繼承,變量無法被修改,方法無法被重寫,并且會進行内聯

結束

  暫時想到的就這麼多了,僅僅是一些開發中的細節部分,有些衆所周知的就沒寫,比如複用

ListView

ConvertView

,壓縮圖檔、異步加載等等,

如有錯誤, 敬請指正, 感激不盡!