Android之开发性能优化简介
原帖地址:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=327340&extra=page%3D1%26filter%3Dauthor%26orderby%3Ddateline
作者:賀小令
? ?? ? 隨著技術的發展,智能手機硬件配置越來越高,可是它和現在的PC相比,其運算能力,續航能力,存儲空間等都還是受到很大的限制,同時用戶對手機的體驗要求遠遠高于PC的桌面應用程序。以上理由,足以需要開發人員更加專心去實現和優化你的代碼了。選擇合適的算法和數據結構永遠是開發人員最先應該考慮的事情。同時,我們應該時刻牢記,寫出高效代碼的兩條基本的原則:(1)不要做不必要的事;(2)不要分配不必要的內存。
我從去年開始接觸Android開發,以下結合自己的一點項目經驗,同時參考了Google的優化文檔和網上的諸多技術大牛給出的意見,整理出這份文檔。
2)避免創建不必要的對象
就像世界上沒有免費的午餐,世界上也沒有免費的對象。雖然gc為每個線程都建立了臨時對象池,可以使創建對象的代價變得小一些,但是分配內存永遠都比不分配內存的代價大。如果你在用戶界面循環中分配對象內存,就會引發周期性的垃圾回收,用戶就會覺得界面像打嗝一樣一頓一頓的。所以,除非必要,應盡量避免盡力對象的實例。下面的例子將幫助你理解這條原則:
當你從用戶輸入的數據中截取一段字符串時,盡量使用substring函數取得原始數據的一個子串,而不是為子串另外建立一份拷貝。這樣你就有一個新的String對象,它與原始數據共享一個char數組。 如果你有一個函數返回一個String對象,而你確切的知道這個字符串會被附加到一個StringBuffer,那么,請改變這個函數的參數和實現方式,直接把結果附加到StringBuffer中,而不要再建立一個短命的臨時對象。
一個更極端的例子是,把多維數組分成多個一維數組:
int數組比Integer數組好,這也概括了一個基本事實,兩個平行的int數組比 (int,int)對象數組性能要好很多。同理,這試用于所有基本類型的組合。如果你想用一種容器存儲(Foo,Bar)元組,嘗試使用兩個單獨的Foo[]數組和Bar[]數組,一定比(Foo,Bar)數組效率更高。(也有例外的情況,就是當你建立一個API,讓別人調用它的時候。這時候你要注重對API接口的設計而犧牲一點兒速度。當然在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);
- }
[color=rgb(51, 102, 153) !important] 復制代碼
最好改成這樣:
- int count = this.mCount;
- Item[] items = this.mItems;
- for(int i =0; i < count; i++)??{
- ? ?? ? dumpItems(items);
- }
[color=rgb(51, 102, 153) !important] 復制代碼
另一個相似的原則是:永遠不要在for的第二個條件中調用任何方法。如下面方法所示,在每次循環的時候都會調用getCount()方法,這樣做比你在一個int先把結果保存起來開銷大很多。
- for(int i =0; i < this.getCount(); i++) {
- dumpItems(this.getItem(i));
- }
[color=rgb(51, 102, 153) !important] 復制代碼
同樣如果你要多次訪問一個變量,也最好先為它建立一個本地變量,例如:
- 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);
- }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
這里有4次訪問成員變量mScrollBar,如果將它緩存到本地,4次成員變量訪問就會變成4次效率更高的棧變量訪問。
另外就是方法的參數與本地變量的效率相同。
1)對常量使用static final修飾符
讓我們來看看這兩段在類前面的聲明:
- static int intVal = 42;
- static String strVal = "Hello, world!";
[color=rgb(51, 102, 153) !important] 復制代碼
必以其會生成一個叫做clinit的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將42賦給intVal,然后把一個指向類中常量表的引用賦給strVal。當以后要用到這些值的時候,會在成員變量表中查找到他們。 下面我們做些改進,使用“final”關鍵字:
- static final int intVal = 42;
- static final String strVal = "Hello, world!";
[color=rgb(51, 102, 153) !important] 復制代碼
現在,類不再需要clinit方法,因為在成員變量初始化的時候,會將常量直接保存到類文件中。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字符串常量,而不是使用成員變量。
將一個方法或類聲明為final不會帶來性能的提升,但是會幫助編譯器優化代碼。舉例說,如果編譯器知道一個getter方法不會被重載,那么編譯器會對其采用內聯調用。
你也可以將本地變量聲明為final,同樣,這也不會帶來性能的提升。使用“final”只能使本地變量看起來更清晰些(但是也有些時候這是必須的,比如在使用匿名內部類的時候)。
2)使用改進的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.mSplat;
- ? ?? ???}
- ? ? }
- ? ? public void one() {
- ? ?? ???int sum = 0;
- ? ?? ???Foo[] localArray = mArray;
- ? ?? ???int len = localArray.length;
- ? ?? ???for (int i = 0; i < len; ++i) {
- ? ?? ?? ?? ?sum += localArray.mSplat;
- ? ?? ???}
- ? ? }
- ? ? public void two() {
- ? ?? ???int sum = 0;
- ? ?? ???for (Foo a : mArray) {
- ? ?? ?? ?? ?sum += a.mSplat;
- ? ?? ???}
- }
- }
[color=rgb(51, 102, 153) !important]復制代碼
在zero()中,每次循環都會訪問兩次靜態成員變量,取得一次數組的長度。
在one()中,將所有成員變量存儲到本地變量。
two()使用了在java1.5中引入的foreach語法。編譯器會將對數組的引用和數組的長度保存到本地變量中,這對訪問數組元素非常好。但是編譯器還會在每次循環中產生一個額外的對本地變量的存儲操作(對變量a的存取)這樣會比one()多出4個字節,速度要稍微慢一些。
3)避免使用浮點數
通常的經驗是,在Android設備中,浮點數會比整型慢兩倍,在缺少FPU和JIT的G1上對比有FPU和JIT的Nexus One中確實如此(兩種設備間算術運算的絕對速度差大約是10倍)
從速度方面說,在現代硬件上,float和double之間沒有任何不同。更廣泛的講,double大2倍。在臺式機上,由于不存在空間問題,double的優先級高于float。
但即使是整型,有的芯片擁有硬件乘法,卻缺少除法。這種情況下,整型除法和求模運算是通過軟件實現的,就像當你設計Hash表,或是做大量的算術那樣,例如a/2可以換成a*0.5。
4)了解并使用類庫
選擇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/ref ... ackage-summary.html
iv.TextUtils類
對于字符串處理Android為我們提供了一個簡單實用的TextUtils類,如果處理比較簡單的內容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類,詳細請參考http://developer.android.com/ref ... 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給的幫助文檔時很有益的。
6)復雜算法盡量用C完成
復雜算法盡量用C或者C++完成,然后用JNI調用。但是如果是算法比較單間,不必這么麻煩,畢竟JNI調用也會花一定的時間。請權衡。
7)減少不必要的全局變量
盡量避免static成員變量引用資源耗費過多的實例,比如Context。Android提供了很健全的消息傳遞機制(Intent)和任務模型(Handler),可以通過傳遞或事件的方式,防止一些不必要的全局變量。
9)了解Java四種引用方式
JDK 1.2版本開始,把對象的引用分為4種級別,從而使程序能更加靈活地控制對象的生命周期。這4種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。
i.強引用(StrongReference)
強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。
ii.軟引用(SoftReference)
如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。
iii.弱引用(WeakReference)
在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由于垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
iv.虛引用(PhantomReference)
顧名思義,就是形同虛設。與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
了解并熟練掌握這4中引用方式,選擇合適的對象應用方式,對內存的回收是很有幫助的。
詳細請參考http://blog.csdn.net/feng88724/article/details/6590064
? ?? ?? ?? ??
10)使用實體類比接口好
假設你有一個HashMap對象,你可以將它聲明為HashMap或者Map:
- Map map1 = new HashMap();
- HashMap map2 = new HashMap();
[color=rgb(51, 102, 153) !important] 復制代碼
哪個更好呢?
按照傳統的觀點Map會更好些,因為這樣你可以改變他的具體實現類,只要這個類繼承自Map接口。傳統的觀點對于傳統的程序是正確的,但是它并不適合嵌入式系統。調用一個接口的引用會比調用實體類的引用多花費一倍的時間。如果HashMap完全適合你的程序,那么使用Map就沒有什么價值。如果有些地方你不能確定,先避免使用 Map,剩下的交給IDE提供的重構功能好了。(當然公共API是一個例外:一個好的API常常會犧牲一些性能)
11)避免使用枚舉
枚舉變量非常方便,但不幸的是它會犧牲執行的速度和并大幅增加文件體積。例如:
- public class Foo {
- ? ?? ? public enum Shrubbery { GROUND, CRAWLING, HANGING }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
會產生一個900字節的.class文件(Foo$Shubbery.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
- ? ?? ? }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
替換為:
- 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
- ? ?? ? }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
會使性能得到一些改善,但這并不是最終的解決之道。
12)在私有內部內中,考慮用包訪問權限替代私有訪問權限
- 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);
- ? ?? ?? ???}
- }
[color=rgb(51, 102, 153) !important] 復制代碼
需要注意的關鍵是:我們定義的一個私有內部類(Foo$Inner),直接訪問外部類中的一個私有方法和私有變量。這是合法的,代碼也會打印出預期的“Value is 27”。
但問題是,虛擬機認為從Foo$Inner中直接訪問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);
- }
[color=rgb(51, 102, 153) !important] 復制代碼
內部類會在外部類中任何需要訪問mValue字段或調用doStuff方法的地方調用這些靜態方法。這意味著這些代碼將直接存取成員變量表現為通過存取器方法訪問。之前提到過存取器訪問如何比直接訪問慢,這例子說明,某些語言約會定導致不可見的性能問題。
如果你在高性能的Hotspot中使用這些代碼,可以通過聲明被內部類訪問的字段和成員為包訪問權限,而非私有。但這也意味著這些字段會被其他處于同一個包中的類訪問,因此在公共API中不宜采用。
13)將與內部類一同使用的變量聲明在包范圍內
請看下面的類定義:
- 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);
- ? ?? ? }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
這其中的關鍵是,我們定義了一個內部類(Foo$Inner),它需要訪問外部類的私有域變量和函數。這是合法的,并且會打印出我們希望的結果Value is 27。
問題是在技術上來講(在幕后)Foo$Inner是一個完全獨立的類,它要直接訪問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);
- }
[color=rgb(51, 102, 153) !important] 復制代碼
內部類在每次訪問mValueg和gdoStuffg方法時,都會調用這些靜態方法。就是說,上面的代碼說明了一個問題,你是在通過接口方法訪問這些成員變量和函數而不是直接調用它們。在前面我們已經說過,使用接口方法(getter、setter)比直接訪問速度要慢。所以這個例子就是在特定語法下面產生的一個“隱性的”性能障礙。
通過將內部類訪問的變量和函數聲明由私有范圍改為包范圍,我們可以避免這個問題。這樣做可以讓代碼運行更快,并且避免產生額外的靜態方法。(遺憾的是,這些域和方法可以被同一個包內的其他類直接訪問,這與經典的OO原則相違背。因此當你設計公共API的時候應該謹慎使用這條優化原則)。
14)緩存
適量使用緩存,不要過量使用,因為內存有限,能保存路徑地址的就不要存放圖片數據,不經常使用的盡量不要緩存,不用時就清空。
15)關閉資源對象
對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/gui ... erarchy-viewer.html
iii.通過Layoutopt優化布局
通過Android sdk中tools目錄下的layoutopt 命令查看你的布局是否需要優化。詳細請參考http://apps.hi.baidu.com/share/detail/34247942
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);
[color=rgb(51, 102, 153) !important] 復制代碼
使用得到的圖片原始寬高計算適合自己的smaplesize
- BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inSampleSize = 4 ;// 4就代表容量變為以前容量的1/4
- Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
[color=rgb(51, 102, 153) !important] 復制代碼
? ?? ?? ?? ?? ?? ???
對于過時的Bitmap對象一定要及時recycle,并且把此對象賦值為null。
- <span style="line-height: 1.5;">bitmap.recycle(); </span>
- bitmap = null;
[color=rgb(51, 102, 153) !important] 復制代碼
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;
- }
[color=rgb(51, 102, 153) !important] 復制代碼
建議改為:
- @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;
- ? ?? ? }
- }
[color=rgb(51, 102, 153) !important] 復制代碼
以上是Google IO大會上給出的優化建議,經過嘗試ListView確實流暢了許多。使用1000條記錄,經測試第一種方式耗時:25211ms,第二種方式耗時:16528ms。
4.結束語
本文給出了一些Android 移動開發中常見的優化方法,多數情況下利用這些優化方法優化后的代碼,執行效率有明顯的提高,內存溢出情況也有所改善。在實際應用中對程序的優化一定要權衡是否是必須的,因為優化可能會帶來諸如增加BUG,降低代碼的可讀性,降低代碼的移植性等不良效果。
希望不要盲目優化,請先確定存在問題,再進行優化。并且你知道當前系統的性能,否則無法衡量你進行嘗試所得到的性能提升。
希望本文能給大家切實帶來幫助。歡迎就上述問題進一步交流。如有發現錯誤或不足,請斧正。
總結
以上是生活随笔為你收集整理的Android之开发性能优化简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android中AutoComplete
- 下一篇: android sina oauth2.