Android之——性能與內存優化

轉載請註明出處:http://blog.csdn.net/l1028386804/article/details/46987951

寫出高效代碼的兩條主要的原則:(1)不要作沒必要要的事;(2)不要分配沒必要要的內存。html

1. 內存優化

      Android系統對每個軟件所能使用的RAM空間進行了限制(如:Nexus one 對每個軟件的內存限制是24M),同一時候Java語言自己比較消耗內存,dalvik虛擬機也要佔用必定的內存空間。因此合理使用內存,彰顯出一個程序猿的素養和技能。java


1) 瞭解JIT

  即時編譯(Just-in-time Compilation。JIT),又稱動態轉譯(Dynamic Translation),是一種經過在執行時將字節碼翻譯爲機器碼,從而改善字節碼編譯語言性能的技術。即時編譯前期的兩個執行時理論是字節碼編譯和動態編譯。Android原來Dalvik虛擬機是做爲一種解釋器實現。新版(Android2.2+)將換成JIT編譯器實現。性能測試顯示,在多項測試中新版本號比舊版本號提高了大約6倍。android

2) 避免建立沒必要要的對象

  就像世界上沒有免費的午飯,世界上也沒有免費的對象。web

儘管gc爲每個線程都創建了暫時對象池。可以使建立對象的代價變得小一些。但是分配內存永遠都比不分配內存的代價大。假設你在用戶界面循環中分配對象內存。就會引起週期性的垃圾回收。用戶就會認爲界面像打嗝同樣一頓一頓的。因此,除非必要。應儘可能避免盡力對象的實例。如下的樣例將幫助你理解這條原則:當你從用戶輸入的數據中截取一段字符串時。儘可能使用substring函數取得原始數據的一個子串,而不是爲子串另外創建一份拷貝。算法

這樣你就有一 個新的String對象,它與原始數據共享一個char數組。 假設你有一個函數返回一個String對象,而你確切的知道這個字符串會被附加到一個StringBuffer。那麼。請改變這個函數的參數和實現方式。直接把結果附加到StringBuffer中,而不要再創建一個短命的暫時對象。
  一個更極端的樣例是。把多維數組分紅多個一維數組:
  int數組比Integer數組好,這也歸納了一個基本事實。兩個平行的int數組比 (int,int)對象數組性能要好很是多。數據庫

同理,這試用於所有基本類型的組合。假設你想用一種容器存儲(Foo,Bar)元組,嘗試使用兩個單獨的 Foo[]數組和Bar[]數組。必定比(Foo,Bar)數組效率更高。(也有例外的狀況。就是當你創建一個API。讓別人調用它的時候。這時候你要注重對API接口的設計而犧牲一點兒速度。canvas

固然在API的內部,你仍要儘量的提升代碼的效率)
  總體來講,就是避免建立短命的暫時對象。下降對象的建立就能下降垃圾收集,進而下降對用戶體驗的影響。數組


3)靜態方法取代虛擬方法

  假設不需要訪問某對象的字段,將方法設置爲靜態,調用會加速15%到20%。這也是一種好的作法。因爲你可以從方法聲明中看出調用該方法不需要更新此對象的狀態。緩存


4)避免內部Getters/Setters

  在源生語言像C++中,一般作法是用Getters(i=getCount())取代直接字段訪問(i=mCount)。這是C++中一個好的習慣,因爲編譯器會內聯這些訪問,並且假設需要約束或者調試這些域的訪問,你可以在不論什麼時間加入代碼。而在Android中,這不是一個好的作法。虛方法調用的代價比直接字段訪問高昂不少。一般依據面嚮對象語言的實踐,在公共接口中使用Getters和Setters是有道理的。但在一個字段經常被訪問的類中宜採用直接訪問。安全

無JIT時,直接字段訪問大約比調用getter訪問快3倍。有JIT時(直接訪問字段開銷等同於局部變量訪問),要快7倍。

5)將成員緩存到本地

訪問成員變量比訪問本地變量慢得多,如下一段代碼:

for(int i =0; i <this.mCount; i++)  {
	dumpItem(this.mItems);
}
最好改爲這樣:

int count = this.mCount;
Item[] items = this.mItems;
for(int i =0; i < count; i++)  {
     dumpItems(items);
}
         還有一個類似的原則是:永遠不要在for的第二個條件中調用不論什麼方法。

如如下方法所看到的,在每次循環的時候都會調用getCount()方法,這樣作比你在一個int先把結果保存起來開銷大很是多。還有一個類似的原則是:永遠不要在for的第二個條件中調用不論什麼方法。如如下方法所看到的,在每次循環的時候都會調用getCount()方法,這樣作比你在一個int先把結果保存起來開銷大很是多。

for(int i =0; i < this.getCount(); i++) {
	dumpItems(this.getItem(i));
}
相同假設你要屢次訪問一個變量。也最好先爲它創建一個本地變量,好比:

protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {
	if(isHorizontalScrollBarEnabled()) {
		intsize = mScrollBar.getSize(false);
		if(size <=0) {
       		size = mScrollBarSize;
		}
		mScrollBar.setBounds(0, height - size, width, height);
		mScrollBar.setParams(computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(),false);
		mScrollBar.draw(canvas);
	}
} 
這裏有4次訪問成員變量mScrollBar。假設將它緩存到本地。4次成員變量訪問就會變成4次效率更高的棧變量訪問。

另外就是方法的參數與本地變量的效率一樣。

6)對常量使用static final修飾符

  讓咱們來看看這兩段在類前面的聲明:

static int intVal = 42;
static String strVal = "Hello, world!";
        必以其會生成一個叫作clinit的初始化類的方法,當類第一次被使用的時候這種方法會被運行。方法會將42賦給intVal,而後把一個指向類中常量表 的引用賦給strVal。當之後要用到這些值的時候,會在成員變量表中查找到他們。 如下咱們作些改進,使用「final」keyword:
static final int intVal = 42;
static final String strVal = "Hello, world!";
現在。類再也不需要clinit方法,因爲在成員變量初始化的時候。會將常量直接保存到類文件裏。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字符串常量。而不是使用成員變量。


  將一個方法或類聲明爲final不會帶來性能的提高,但是會幫助編譯器優化代碼。舉例說,假設編譯器知道一個getter方法不會被重載,那麼編譯器會對其採用內聯調用。


  你也可以將本地變量聲明爲final,相同。這也不會帶來性能的提高。

使用「final」僅僅能使本地變量看起來更清晰些(但是也有些時候這是必須的,比方在使用匿名內部類的時候)。

7)使用改進的For循環語法

  改進for循環(有時被稱爲for-each循環)能夠用於實現了iterable接口的集合類及數組中。

在集合類中。迭代器讓接口調用 hasNext()和next()方法。

在ArrayList中。手寫的計數循環迭代要快3倍(無論有沒有JIT),但其它集合類中。改進的for循環語 法和迭代器具備一樣的效率。如下展現集中訪問數組的方法:

static class Foo {
        int mSplat;
    }
    Foo[] mArray = ...
    public void zero() {
        int sum = 0;
        for (int i = 0; i < mArray.length; ++i) {
            sum += mArray[i].mSplat;
        }
    }
    public void one() {
        int sum = 0;
        Foo[] localArray = mArray;
        int len = localArray.length;
  
        for (int i = 0; i < len; ++i) {
            sum += localArray[i].mSplat;
        }
    }
    public void two() {
        int sum = 0;
        for (Foo a : mArray) {
            sum += a.mSplat;
        }
	}
}
        在zero()中,每次循環都會訪問兩次靜態成員變量,取得一次數組的長度。


  在one()中,將所有成員變量存儲到本地變量。
  two()使用了在java1.5中引入的foreach語法。

編譯器會將對數組的引用和數組的長度保存到本地變量中,這對訪問數組元素很好。 但是編譯器還會在每次循環中產生一個額外的對本地變量的存儲操做(對變量a的存取)這樣會比one()多出4個字節,速度要略微慢一些。

8)避免使用浮點數

  一般的經驗是。在Android設備中,浮點數會比整型慢兩倍。在缺乏FPU和JIT的G1上對照有FPU和JIT的Nexus One中確實如此(兩種設備間算術運算的絕對速度差大約是10倍)從速度方面說,在現代硬件上,float和double之間沒有不論什麼不一樣。更普遍的講,double大2倍。

在臺式機上,由於不存在空間問題,double的優先級高於float。但即便是整型。有的芯片擁有硬件乘法,卻缺乏除法。這樣的狀況下。整型除法和求模運算是經過軟件實現的,就像當你設計Hash表,或是作大量的算術那樣,好比a/2可以換成a*0.5。

9)瞭解並使用類庫

  選擇Library中的代碼而非本身重寫。除了一般的那些緣由外,考慮到系統空暇時會用匯編代碼調用來替代library方法,這可能比JIT中生成的等價的最好的Java代碼還要好。



           i. 當你在處理字串的時候,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊實現的方法。這些方法都是使用C/C++實現的,比起Java循環快10到100倍。



           ii.System.arraycopy方法在有JIT的Nexus One上,自行編碼的循環快9倍。

           iii.android.text.format包下的Formatter類,提供了IP地址轉換、文件大小轉換等方法;DateFormat類,提供了各類時間轉換,都是很高效的方法。
      具體請參考 http://developer.android.com/reference/android/text/format/package-summary.html
           iv.TextUtils類
      對於字符串處理Android爲咱們提供了一個簡單有用的TextUtils類,假設處理比較簡單的內容不用去思考正則表達式最好仍是試試這個在android.text.TextUtils的類,具體請參考http://developer.android.com/reference/android/text/TextUtils.html
            v.    高性能MemoryFile類。


       很是多人抱怨Android處理底層I/O性能不是很是理想。假設不想使用NDK則可以經過MemoryFile類實現高性能的文件讀寫操做。MemoryFile適用於哪些地方呢?對於I/O需要頻繁操做的,主要是和外部存儲相關的I/O操做,MemoryFile經過將 NAND或SD卡上的文件,分段映射到內存中進行改動處理,這樣就用快速的RAM取代了ROM或SD卡。性能天然提升很多,對於Android手機而言同一時候還下降了電量消耗。

該類實現的功能不是很是多,直接從Object上繼承,經過JNI的方式直接在C底層運行。
具體請參考 http://developer.android.com/reference/android/os/MemoryFile.html
在此,僅僅簡單列舉幾個常用的類和方法,不少其它的是要靠平時的積累和發現。

多閱讀Google給的幫助文檔時很是故意的。

10) 合理利用本地方法

  本地方法並不是必定比Java高效。最起碼,Java和native之間過渡的關聯是有消耗的,而JIT並不能對此進行優化。當你分配本地資源時 (本地堆上的內存,文件說明符等),每每很是難實時的回收這些資源。

同一時候你也需要在各類結構中編譯你的代碼(而非依賴JIT)。

甚至可能需要針對一樣的架構 來編譯出不一樣的版本號:針對ARM處理器的GI編譯的本地代碼,並不能充分利用Nexus One上的ARM。而針對Nexus One上ARM編譯的本地代碼不能在G1的ARM上執行。當你想部署程序到存在本地代碼庫的Android平臺上時。本地代碼才顯得尤其實用。而並不是爲了Java應用程序的提速。

11)複雜算法儘可能用C完畢

  複雜算法儘可能用C或者C++完畢,而後用JNI調用。

但是假設是算法比較單間。沒必要這麼麻煩,畢竟JNI調用也會花必定的時間。請權衡。

12) 下降沒必要要的全局變量

  儘可能避免static成員變量引用資源耗費過多的實例,比方Context。Android提供了很是健全的消息傳遞機制(Intent)和任務模型(Handler),可以經過傳遞或事件的方式。防止一些沒必要要的全局變量。

13) 不要過多期望gc

  Java的gc使用的是一個有向圖,推斷一個對象是否有效看的是其它的對象能到達這個對象的頂點,有向圖的相對於鏈表、二叉樹來講開銷是可想而知。因此不要過多期望gc。

將不用的對象可以把它指向NULL。並注意代碼質量。同一時候,顯示讓系統gc回收,好比圖片處理時,

if(bitmap.isRecycled()==false) { //假設沒有回收
     bitmap.recycle();
}

14)瞭解Java四種引用方式

  JDK 1.2版本號開始,把對象的引用分爲4種級別,從而使程序能更加靈活地控制對象的生命週期。這4種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。
    i.強引用(StrongReference)
   強引用是使用最廣泛的引用。假設一個對象具備強引用,那垃圾回收器毫不會回收它。

當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止。也不會靠任意回收具備強引用的對象來解決內存不足的問題。
    ii.軟引用(SoftReference)
   假設一個對象僅僅具備軟引用,則內存空間足夠,垃圾回收器就不會回收它;假設內存空間不足了,就會回收這些對象的內存。僅僅要垃圾回收器沒有回收它,該對象就可以被程序使用。

軟引用可用來實現內存敏感的快速緩存。
    iii.弱引用(WeakReference)
 在垃圾回收器線程掃描它所管轄的內存區域的過程當中。一旦發現了僅僅具備弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。

只是,由於垃圾回收器是一個優先級很是低的線程,所以不必定會很是快發現那些僅僅具備弱引用的對象。


     iv.虛引用(PhantomReference)
    顧名思義,就是形同虛設。與其它幾種引用都不一樣,虛引用並不會決定對象的生命週期。

假設一個對象僅持有虛引用,那麼它就和沒有不論什麼引用同樣,在不論何時均可能被垃圾回收器回收。

瞭解並熟練掌握這4中引用方式,選擇合適的對象應用方式,對內存的回收是很是有幫助的

15)使用實體類比接口好

若是你有一個HashMap對象,你可以將它聲明爲HashMap或者Map:

Map map1 = new HashMap();
HashMap map2 = new HashMap();
哪一個更好呢?
依照傳統的觀點Map會更好些,因爲這樣你可以改變他的詳細實現類,僅僅要這個類繼承自Map接口。傳統的觀點對於傳統的程序是正確的,但是它並不適合嵌入式系統。

調用一個接口的引用會比調用實體類的引用多花費一倍的時間。假設HashMap全然適合你的程序,那麼使用Map就沒有什麼價值。假設有些地方你不能肯定,先避免使用Map。剩下的交給IDE提供的重構功能好了。(固然公共API是一個例外:一個好的API常常會犧牲一些性能)

16)避免使用枚舉

枚舉變量很方便,但不幸的是它會犧牲運行的速度和並大幅添加文件體積。好比:

public class Foo {
    public enum Shrubbery { GROUND, CRAWLING, HANGING }
}
會產生一個900字節的.class文件(FooShubbery.class)。在它被首次調用時。這個類會調用初始化方法來準備每個枚舉變量。每個枚舉項都會被聲明成一個靜態變量。並被賦值。

而後將這些靜態變量放在一個名爲」VALUES」的靜態數組變量中。而這麼一大堆代碼。不過爲了使用三個整數。
這樣:Shrubbery shrub =Shrubbery.GROUND;會引發一個對靜態變量的引用,假設這個靜態變量是final int,那麼編譯器會直接內聯這個常數。
         一方面說。使用枚舉變量可以讓你的API更出色,並能提供編譯時的檢查。因此在一般的時候你毫無疑問應該爲公共API選擇枚舉變量。

但是當性能方面有所限制的時候,你就應該避免這樣的作法了。
        有些狀況下。使用ordinal()方法獲取枚舉變量的整數值會更好一些,舉例來講:

for(int n =0; n < list.size(); n++) {
       if(list.items[n].e == MyEnum.VAL_X) {
              // do something
       } else if(list.items[n].e == MyEnum.VAL_Y) {
              // do something
       }
}
替換爲:

int valX = MyEnum.VAL_X.ordinal();
int valY = MyEnum.VAL_Y.ordinal();
int count = list.size();
MyItem items = list.items();
for(int n =0; n < count; n++) {
       intvalItem = items[n].e.ordinal();
       if(valItem == valX) {
              // do something
       } else if(valItem == valY) {
              // do something
       }
}
會使性能獲得一些改善。但這並不是終於的解決之道。

17)在私有內部內中,考慮用包訪問權限替代私有訪問權限

public class Foo {
           public class Inner {
                public void stuff() {
                       Foo.this.doStuff(Foo.this.mValue);
                }
           }
           private int mValue;
           public void run() {
                Inner in = new Inner();
                mValue = 27;
                in.stuff();
           }
           private void doStuff(int value) {
                 System.out.println("value:"+value);
           }
}
         需要注意的關鍵是:咱們定義的一個私有內部類(FooInner),直接訪問外部類中的一個私有方法和私有變量。這是合法的。代碼也會打印出預期的「Valueis27」。但問題是,虛擬機以爲從FooInner中直接訪問Foo的私有成員是非法的,因爲他們是兩個不一樣的類。雖然Java語言贊成內部類訪問外部類的私有成員,但是經過編譯器生成幾個綜合方法來橋接這些間隙的

/*package*/
static int Foo.access$100(Foo foo) {
   return foo.mValue;
}
/*package*/
static void Foo.access%200(Foo foo,int value) {
     foo.duStuff(value);
}
        內部類會在外部類中不論什麼需要訪問mValue字段或調用doStuff方法的地方調用這些靜態方法。這意味着這些代碼將直接存取成員變量表現爲經過存取器方法訪問。

以前提到過存取器訪問怎樣比直接訪問慢。這樣例說明。某些語言約會定致使不可見的性能問題。

假設你在高性能的Hotspot中使用這些代碼,可以經過聲明被內部類訪問的字段和成員爲包訪問權限,而非私有。

但這也意味着這些字段會被其它處於同一個包中的類訪問,所以在公共API中不宜採用。

18)將與內部類一同使用的變量聲明在包範圍內

請看如下的類定義:

public class Foo {
       private class Inner {
           void stuff() {
               Foo.this.doStuff(Foo.this.mValue);
           }
       }
 
       private int mValue;
       public void run() {
           Inner in = new Inner();
           mValue = 27;
           in.stuff();
       }
 
       private void doStuff(int value) {
           System.out.println("Value is " + value);
       }
}
         這當中的關鍵是,咱們定義了一個內部類(FooInner),它需要訪問外部類的私有域變量和函數。這是合法的,並且會打印出咱們但願的結果Valueis27。問題是在技術上來說(在幕後)FooInner是一個全然獨立的類。它要直接訪問Foo的私有成員是非法的。要跨越這個鴻溝,編譯器需要生成一組方法

/*package*/ static int Foo.access$100(Foo foo) {
    return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
    foo.doStuff(value);
}
         內部類在每次訪問mValueg和gdoStuffg方法時,都會調用這些靜態方法。就是說,上面的代碼說明了一個問題,你是在經過接口方法訪問這些成員變量和函數而不是直接調用它們。

在前面咱們已經說過,使用接口方法(getter、setter)比直接訪問速度要慢。因此這個樣例就是在特定語法如下產生的一個「隱性的」性能障礙。

經過將內部類訪問的變量和函數聲明由私有範圍改成包範圍,咱們可以避免這個問題。這樣作可以讓代碼執行更快。並且避免產生額外的靜態方法。

(遺憾的是,這些域和方法可以被同一個包內的其它類直接訪問,這與經典的OO原則相違背。

所以當你設計公共API的時候應該慎重使用這條優化原則)。

19)緩存

  適量使用緩存,不要過量使用。因爲內存有限,能保存路徑地址的就不要存放圖片數據。不經常使用的儘可能不要緩存,不用時就清空。


20)關閉資源對象

       對SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O操做等都應該記得顯示關閉。

2.視圖優化

1)View優化

    i.    下降沒必要要的View以及View的嵌套層次。
  比方實現一個listview中常用的layout。可以使用RelativeLayout下降嵌套,要知道每個View的對象會耗費1~2k內存。嵌套層次過多會引發頻繁的gc,形成ANR。
    ii.    經過HierarchyViewer查看佈局結構
利用HierarchyViewer來查看View的結構:~/tools/hierarchyviewer。能很是清楚地看到RelativeLayout如下的扁平結構,這樣能加快dom的渲染速度。


具體請參考 http://developer.android.com/guide/developing/tools/hierarchy-viewer.html
    iii.    經過Layoutopt優化佈局
  經過Android sdk中tools文件夾下的layoutopt 命令查看你的佈局是否需要優化

2)多線程解決複雜計算

  佔用CPU較多的數據操做盡量放在一個單獨的線程中進行,經過handler等方式把運行的結果交於UI線程顯示。

特別是針對的網絡訪問,數據庫查詢。和複雜的算法。

眼下Android提供了AsyncTask,Hanlder、Message和Thread的組合。

對於多線程的處理。假設併發的線程很是多,同一時候有頻繁的建立和釋放,可以經過concurrent類的線程池解決線程建立的效率瓶頸。另外值得注意的是。應用開發中本身定義View的時候,交互部分。千萬不要寫成線程不斷刷新界面顯示,而是依據TouchListener事件主動觸發界面的更新。

3)佈局用Java完畢比XML快

  普通狀況下對於Android程序佈局每每使用XML文件來編寫。這樣可以提升開發效率,但是考慮到代碼的安全性以及運行效率,可以經過Java代碼運行建立,儘管Android編譯過的XML是二進制的。但是載入XML解析器的效率對於資源佔用仍是比較大的,Java處理效率比XML快得多,但是對於一個複雜界面的編寫,可能需要一些套嵌考慮,假設你思惟靈活的話。使用Java代碼來佈局你的Android應用程序是一個更好的方法。


4)對大型圖片進行縮放

  圖片讀取是OOM(Out of Memory)的常客,當在Android手機上直接讀取4M的圖片時,死神通常都會降臨,因此致使每每本身手機拍攝的照片都不能直接讀取。對大型圖片進行縮放處理圖片時咱們經常會用到BitmapFactory類。android系統中讀取位圖Bitmap時分給虛擬機中圖片的堆棧大小僅僅有8M。用BitmapFactory解碼一張圖片時。有時也會遇到該錯誤。

這每每是由於圖片過大形成的。這時咱們需要分配更少的內存空間來存儲。BitmapFactory.Options.inSampleSize設置恰當的inSampleSize可以使BitmapFactory分配更少的空間以消除該錯誤。Android提供了一種動態計算的,例如如下:

讀取圖片以前先查看其大小:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
使用獲得的圖片原始寬高計算適合本身的smaplesize

BitmapFactory.Options opts = new BitmapFactory.Options();
 opts.inSampleSize = 4 ;// 4就表明容量變爲曾經容量的1/4
 Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 
對於過期的Bitmap對象必定要及時recycle。並且把此對象賦值爲null。
bitmap.recycle();
bitmap = null; 

5)合理使用ViewStub

  ViewStub 是一個隱藏的,不佔用內存空間的視圖對象,它可以在執行時延遲載入佈局資源文件。

當ViewStub可見,或者調用 inflate()函數時,纔會載入這個佈局資源文件。 該ViewStub在載入視圖時在父容器中替換它自己。所以。ViewStub會一直存在於視圖中,直到調用setVisibility(int) 或者inflate()爲止。

ViewStub的佈局參數會隨着載入的視圖數一同被加入到ViewStub父容器。相同。你也可以經過使用 inflatedId屬性來定義或重命名要載入的視圖對象的Id值。

因此咱們可以使用ViewStub延遲載入某些比較複雜的layout,動態載入 View。採用ViewStub 避免一些不經常的視圖長期握住引用。
具體請參考http://developer.android.com/reference/android/view/ViewStub.html

6)針對ListView的性能優化

     i. 複用convertView。
     ii.在getItemView中,推斷convertView是否爲空,假設不爲空,可複用。

假設couvertview中的view需要加入 listerner,代碼必定要在if(convertView==null){}以外。
     iii.異步載入圖片,item中假設包括有web image,那麼最好異步載入。
     iv.高速滑動時不顯示圖片
   當高速滑動列表時(SCROLL_STATE_FLING),item中的圖片或獲取需要消耗資源的view,可以不顯示出來。而處於其它兩種狀 態(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL)。則將那些view顯示出來。
     v.    item儘量的下降使用的控件和佈局的層次。背景色與cacheColorHint設置一樣顏色;ListView中item的佈局相當重要。必須儘可 能的下降使用的控件。佈局。

RelativeLayout是絕對的利器,經過它可以下降佈局的層次。同一時候要儘量的複用控件,這樣可以下降ListView的內存使用,下降滑動時gc次數。

ListView的背景色與cacheColorHint設置一樣顏色。可以提升滑動時的渲染性能。
     vi. getView優化
   ListView中getView是性能是關鍵,這裏要儘量的優化。getView方法中要重用view。getView方法中不能作複雜的邏輯計算,特別是數據庫和網絡訪問操做,不然會嚴重影響滑動時的性能。

優化例如如下所看到的:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
       Log.d("MyAdapter", "Position:" + position + "---" + String.valueOf(System.currentTimeMillis()));
       final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       View v = inflater.inflate(R.layout.list_item_icon_text, null);
       ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
       ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
       return v;
}
建議改成:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
       Log.d("Adapter", "Position:" + position + " : " + String.valueOf(System.currentTimeMillis()));
       ViewHolder holder;
       if (convertView == null) {
              final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
              convertView = inflater.inflate(R.layout.list_item_icon_text, null);
              holder = new ViewHolder();
             holder.icon = (ImageView) convertView.findViewById(R.id.icon);
             holder.text = (TextView) convertView.findViewById(R.id.text);
             convertView.setTag(holder);
       } else {
             holder = (ViewHolder) convertView.getTag();
       }
              holder.icon.setImageResource(R.drawable.icon);
              holder.text.setText(mData[position]);
              return convertView;
       }
 
       static class ViewHolder {
               ImageView icon;
               TextView text;
       }
}
以上是Google IO大會上給出的優化建議。通過嘗試ListView確實流暢了不少。使用1000條記錄。經測試第一種方式耗時:25211ms,另一種方式耗時:16528ms。


7)其它

   i.分辨率適配
   -ldpi,-mdpi, -hdpi配置不一樣精度資源。系統會依據設備自適應,包含drawable, layout,style等不一樣資源。
   ii.儘可能使用dp(density independent pixel)開發,不用px(pixel)。


   iii.多用wrap_content, fill_parent
   iv.拋棄AbsoluteLayout
   v.使用9patch(經過~/tools/draw9patch.bat啓動應用程序),png格式
   vi.採用<merge> 優化佈局層數;採用<include >來共享佈局。


   vii. 將Acitivity中的Window的背景圖設置爲空。

getWindow().setBackgroundDrawable(null);android的默認背景是否是爲空。
   viii.View中設置緩存屬性.setDrawingCache爲true。

3.網絡優化

1)避免頻繁網絡請求

  訪問server端時,創建鏈接自己比傳輸需要跟多的時間。如非必要。不要將一交互可以作的事情分紅屢次交互(這需要與Server端協調好)。有效管理Service 後臺服務就至關於一個持續執行的Acitivity,假設開發的程序後臺都會一個service不停的去服務器上更新數據,在不更新數據的時候就讓它sleep,這樣的方式是很耗電的,一般狀況下。可以使用AlarmManager來定時啓動服務。例如如下所看到的,第30分鐘執行一次。

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
long interval = DateUtils.MINUTE_IN_MILLIS * 30;
long firstWake = System.currentTimeMillis() + interval;
am.setRepeating(AlarmManager.RTC,firstWake,  interval,  pendingIntent);

2)數據壓縮

數據傳輸通過壓縮 眼下大部門站點都支持GZIP壓縮,因此在進行大數據量下載時,儘可能使用GZIP方式下載。可以下降網絡流量,一般是壓縮前數據大小的30%左右。

HttpGet request = new HttpGet("http://example.com/gzipcontent");
HttpResponse resp = new DefaultHttpClient().execute(request);
HttpEntity entity = response.getEntity();
InputStream compressed = entity.getContent();
InputStream rawData = new GZIPInputStream(compressed);

3)使用線程池

  線程池,分爲核心線程池和普通線程池。下載圖片等耗時任務放置在普通線程池,避免耗時任務堵塞線程池後。致使所有異步任務都必須等待。

4)選擇合適的數據格式傳輸形式

當中Tree Parse 是DOM解析 Event/Stream是SAX方式解析。


很是明顯。使用流的方式解析效率要高一些,因爲DOM解析是在對整個文檔讀取完後,再依據節點層次等再組織起來。

而流的方式是邊讀取數據邊解析,數據讀取完後,解析也就完成了。在數據格式方面,JSON和Protobuf效率明顯比XML好很是多。XML和JSON你們都很是熟悉。從上面的圖中可以得出結論就是儘可能使用SAX等邊讀取邊解析的方式來解析數據。針對移動設備,最好能使用JSON之類的輕量級數據格式爲佳。

5)其它

  設置鏈接超時時間和響應超時時間。

Http請求依照業務需求。分爲可否夠緩存和不可緩存。那麼在無網絡的環境中。仍然經過緩存的HttpResponse瀏覽部分數據。實現離線閱讀。

4.數據庫相關

1)相對於封裝過的ContentProvider而言,使用原始SQL語句運行效率高。比方用法rawQuery、execSQL的運行效率比較高。

2)對於需要一次性改動多個數據時。可以考慮使用SQLite的事務方式批量處理。

3)批量插入多行數據使用InsertHelper或者bulkInsert方法

4)有些能用文件操做的,儘可能採用文件操做,文件操做的速度比數據庫的操做要快10倍左右。

5.性能測試

  對於Android平臺上軟件的性能測試可以經過下面幾種方法來分析效率瓶頸,眼下Google在Android軟件開發過程當中已經引入了多種測試工具包。比方Unit測試project,調試類,還有模擬器的Dev Tools都可以直接反應運行性能。

1)在模擬器上的Dev Tools可以激活屏幕顯示當前的FPS,CPU使用率,可以幫助咱們測試一些3D圖形界面的性能。

2)通常涉及到網絡應用的程序,在效率上和網速有很是多關係,這裏需要屢次的調試才幹實際瞭解。

3)對於邏輯算法的效率運行。咱們使用Android上最廣泛的。計算運行時間來查看:

long start = System.currentTimeMillis();
// do something
long duration = System.currentTimeMillis() - start;
終於duration保存着實際處理該方法需要的毫秒數。

4)gc效率跟蹤

      假設你運行的應用比較簡單,可以在DDMS中查看下Logcat的VM釋放內存狀況。大概模擬下那些地方可以緩存數據或改進算法的。

5)線程的使用和同步

      Android平臺上給咱們提供了豐富的多任務同步方法,但在深層上並無過多的比方自旋鎖等高級應用,不 過對於Service和 appWidget而言,他們實際的產品中都應該以多線程的方式處理,以釋放CPU時間,對於線程和堆內存的查看這些都可以在DDMS中看到。

6)利用traceview和monkey等工具測試應用。

7)利用layoutopt和ninepatch等工具優化視圖。

相關文章
相關標籤/搜索