




已閱讀5頁,還剩12頁未讀, 繼續免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
Android代碼優化【轉】2010-08-20 23:40為性能設計:1)避免創建對象對象的創建從來不是免費的。雖然GC使得內存申請代價不再高昂,但是申請總是比不申請來得昂貴。如果你在一個用戶接口循環中申請對象,你將會強行執行周期性的GC,在用戶體驗上出現一些小的“打嗝”,因此除非不得已,你應該避免創建對象實例,下面是一些例子可以幫助理解:當你在一組輸入數據中抽取字符串時,嘗試返回源數據的子串,而非創建一個副本。你將會創建一個新的String對象,但是它會和數據共享字符數組char。如果你有一個返回String的方法,而且你知道它的結果將會一直被追加到StringBuffer,改變你的簽名和實現,使這個函數里面直接追加,避免創建臨時對象。 一個更激進的主意是將多維數組切成與之平行的一維數組:一個int數組比Integer數組要好,但也有一個公認的事實就是兩個平行的int數組要比一個(int,int)對象數組要高效很多。對于其它原始數據類型亦如是。如果你需要實現一個存儲一組對象(Foo,Bar)的容器,請記住兩個平等的Foo和Bar數組通常元比一個定制對象數組要好(當然,對于此有個例外,就是當你設計一個API供其它代碼訪問時;在那樣的情況下,通常最好是為保證API的正確性而犧牲一點速度。但是在你的內部代碼,你應該盡可能保持高效)。通常來說,避免創建臨時對象,如果你可以的話。更少的對象創建意味著更小頻率的GC,這對用戶體驗有直接的影響。2)用Native方法當處理字符串時,要毫不猶豫地使用諸如String.indexOf()、String.lastIndexOf()之類的專門方法,這些是典型的用C/C+代碼實現的方法,它們可以輕易地比實現同樣功能的Java循環快10-100倍。對此建議的一反面是調用一個native方法要比調用一個解析的方法,不要將native方法用于瑣碎的計算,如果可以避免的話。優先使用Virtual而非Interface假如你有一個HashMap對象,你可以聲明它為一個HashMap或一個通用的Map:Map myMap1 = new HashMap();HashMap myMap2 = new HashMap();哪一個更好?一般的會說你該選擇Map,因為它允許你改變其實現,對于通常的編程來說這是對的,但是對于嵌入式系統來說這并不是太妙。通過接口的引用來調用一個方法要比通過一個具體類型的引用調用virtual方法花多2倍的時間。如果你已經選擇了一個HashMap,因為它正好適用你正在做的事情,那通過Map來調用就沒有什么價值了。考慮到IDE可以為你重構代碼,用Map來調用就沒有太大價值了,即使你不知道你代碼將去向何方(但是,再一次的,公共的API是又是一個例外:好的API較少考慮性能)。3)優先選擇static而非virtual如果你不必訪問一個對象的字段,使你的方法成為static方法。它可以被更快地調用,因為它不需要一個虛擬方法表間接調用。同時它也是一個好的做法,因為從方法的簽名可以看出調用這個方法不會改變對象的狀態。4)避免內部的Getter/Setter在一些像C+的語言中,通常的做法是用getter(如:i=getCount())代替直接地訪問字段(i=mCount),在C+這是一個很好的習慣,因為編譯器通常能夠內聯這個訪問,并且你需要限制或debug字段訪問,你可以在任何時候增加代碼。在Android,這是一個壞主意。虛擬方法調用代價是昂貴的,實例字段查找代價更高。沿用面一般向對象編程實踐在公開接口中提供gettter和setter是合理的,但在一個類中你應該直接訪問字段。Cache字段查找訪問對象字段要比訪問本地變量慢得多,如下面這段:for (int i = 0; i this.mCount; i+)dumpItem(this.mItemsi);應該寫成這樣:int count = this.mCount;Item items = this.mItems;for (int i = 0; i count; i+)dumpItems(itemsi);(我們用一個顯式的”this”來表明這是一個成員變量。)有一個相似的指引就是,不要在for語句中的第二個從句中調用方法。例如下面這段代碼將會在每次迭代中都會執行一次getCount(),這是一個巨大的浪費,你可以將它的值cache為一個int。for (int i = 0; i this.getCount(); i+) dumpItems(this.getItem(i);通常,如果你將要訪問一個實例字段多次,一個好的習慣就是創建一個臨時變量。例如:protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) if (isHorizontalScrollBarEnabled() int size = mScrollBar.getSize(false); if (size = 0) size = mScrollBarSize; mScrollBar.setBounds(0, height size, width, height); mScrollBar.setParams( computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); mScrollBar.draw(canvas); 這是對成員字段mScrollBar的四次分開查找,通過將mScrollBar緩存到本地變量,四次成員字段查找變成四次本地變量引用,這樣更為高效。同樣地,方法參數作為本地變量擁有相同的性能特征。聲明常量為final考慮在類開頭的如下聲明:static int intVal = 42;static String strVal = “Hello, world!”;編譯器產生一個叫的類初始化器方法,它在類首次使用時被執行。這個方法將42存到intVal,并為intVal從類文件字符串常量表中抽出一個引用,當這些值在后面被引用到時,它們以字段查找的方式被訪問。我們可以用final關鍵字改進之:static final int intVal = 42;static final String strVal = “Hello, world!”;這個類不再需要一個方法,因為常量存到直接由VM處理的類文件靜態字段初始化器,代碼訪問intVal將會直接使用integer值42,而對intVal的訪問會用一個相對廉價的“字符串常量”指令來代替一個字段查找。聲明一個方法或類為final并不能直接獲得性能上的好處,但它確實能起到某些優化作用。例如,假如編譯器知道一個”getter”不能被一個子類重寫,它能夠內聯這個方法調用。你也可以將本地變量聲明為final,然而這并無真正意義上的性能提升。對于要地變量,只有在使代碼更清晰(或你不得不,如為了在匿名內部類中使用)時使用final。小心使用增強的For循環語句增強的For語句可以用于實現了Iterable接口的Collection,對于這些對象,一個iterator被申請用來進行接口調用hasNext()和next()。對于ArrayList,你最好直接遍歷它,但對于其它collections,增強的For循環語句將會等同于顯式的迭代用法。盡管如此,下面的代碼展示了增強的For語句的可為人接受的用法:public class Foo int mSplat; static Foo mArray = new Foo27; public static void zero() int sum = 0; for (int i = 0; i mArray.length; i+) sum += mArrayi.mSplat; public static void one() int sum = 0; Foo localArray = mArray; int len = localArray.length; for (int i = 0; i len; i+) sum += localArrayi.mSplat; public static void two() int sum = 0; for (Foo a: mArray) sum += a.mSplat; zero()在循環中每次迭代獲取靜態字段兩次計算數組長度一次。one()將所有東西存到本地變量,避免查找。two()用到了增強的For循環語句,由編譯器產生的代碼拷貝數組引用和數組長度到本地變量,使之成為一個遍歷數組元素的一個很好的選擇。它確實在主循環中產生了一個額外的本地載入/存儲,使得它比起one()有點慢并且長了bytes。總之,增強的For語句對于數組表現良好,但對iterable對象要小心使用,因為有額外的對象創建。避免Enum類型Enum非常方便,但不幸的是當考慮到時間和速度時就讓人痛苦。例如這個:public class Foo public enum Shrubbery GROUND, CRAWLING, HANGING 將編譯成一個900byte的.class文件,在首次使用是時,類初始化器在代表每個被枚舉的值對象上激活方法。每個對象都有其靜態字段,并且整個集合就存儲在一個數組(一個稱為“$values”的靜態字段)上,對于僅僅的三個integer來說,那是太多的代碼和數據了。這個:Shrubbery shrub = Shrubbery.GROUND;導致了一次靜態字段查找。如果“GROUND”是一個static final int編譯器將會將它看作是一個常量并內聯它。相反地,當然,是運用enum你可以得到一個更優雅的API和一些編譯時的值檢查。因此,通常折衷的辦法是:為API,你應該千方百計地使用enum,但是當考慮到性能時嘗試避免使用它。利用內部類使用包作用方域考慮下面的類定義:public class Foo 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); private class Inner void stuff() Foo.this.doStuff(Foo.this.mValue); 在這里我們要特別指出的是這里定義了一個內部類(Foo$Inner),它可以直接訪問外部類的私有方法和私有實例字段,這是合法的,代碼的執行的結果是如預期般的“Value is 27”。問題在于,Foo$Inner是一個完全獨立的類,這使得直接訪問其私有方法是非法的,為了架起橋梁,編譯器會產生如下兩個虛擬方法/*package*/ static int Foo.access$100(Foo foo) return foo.mValue;/*package*/ static void Foo.access$200(Foo foo, int value) foo.doStuff(value);當內部類代碼需要訪問外部類的mValue變量或激活doStuff方法時就會調用這些方法。這就意味著上面的代碼清楚表明了你是通過訪問器來訪問成員字段的,而非直接訪問。前面我們討論過訪問器是比直接訪問是要慢的,所以這是一個由于某種特定語言方言所導致的隱性性能打擊。我們可以通過聲明由內部類訪問的字段和方法為具有包作用域而非私有作用域來解決這個問題。這樣運行得更快并且移除了額外產生的方法(不幸的是,這也意味著這些字段可以被同包下的其它類所訪問,這個是違反了使所有的字段成為私有的標準OO做法的,再一次的,如果你是在設計一個公共的API的話,你可能要慎重地考慮這一優化策略)。)避免使用Float類型在Pentium CPU發布之前,對于游戲作者來說做很多整型計算是很正常的事。有了Pentium之后,浮點計算聯合處理器成了內置功能,你的游戲通過交錯整型和浮點操作比只有整型計算時運行起來要快得多。在桌面系統上通常的可以自由的使用浮點數。不幸的是,嵌入式處理器很少具有硬件浮點支持,所以所有的”float”和”double”操作都是在軟件上進行。某些基本的浮點操作可能會花費數微秒。還有,甚至對于整型數,一些芯片支持硬件乘法但缺少硬件除法,在這種情況下,整型除法和取模運算是在軟件上執行的如果你是在設計一個哈希表或做很多數學運算這就是你需要考慮的事情。為響應靈敏性設計代碼可能通過各種性能測試,但是當用戶使用時還是會需要漫長的等待,這些就是那種響應不夠靈敏的應用它們反應遲鈍,掛起或凍住周期很長,或者要花很長時間來處理輸入。在Android上,系統通過向用戶顯示一個稱為應用無響應(ANR:Application Not Responding)的對話框來防止在一段時間內響應不夠快。用戶可以選擇讓應用繼續,但是用戶并不會想要每次都來處理這個對話框。因此應把你的應用設計得響應靈敏,使得系統不必顯示ANR給用戶。通常地,當不能響應用戶輸入時系統顯示一個ANR。例如,如果一個應用在IO操作(經常是網絡訪問)上阻塞了,那么主應用線程就會無法處理正在進行的用戶輸入事件。經過一段時間,系統認為應用已經掛起,向用戶顯示一個ANR,讓用戶可以選擇關閉。相同地,如果你的應用花太多的時間在構建詳細的內存結構上,又或者在計算游戲的下一個動作上,系統會認為你的應用已經掛起。用上面的技術來保證這些計算是高效的一直都是很重要的,但是即使是最高效的代碼運行也是需要花費時間的。在這兩種情況下,解決的辦法通常就是創建一個子線程,在這個子線程上做你的大部分工作。這樣讓你的主線程(驅動用戶接口事件循環)保持運行,并讓你的代碼免于被系統認為已經凍住。因為這樣的線程化通常都是在類級別上來完成的,所以你可以認為響應性能問題是一個類問題(與上面描述的方法級別的性能問題)。這個文檔討論了Android系統是如何決定一個應用沒有響應的,并提供了指引來保障你的應用是響應靈敏的。1)是什么引發了ANR?在Android系統上,應用的響應靈敏性由Activity Manager和Window Manager system services所監控,當它監測到如下的其中一個條件時,Android就會為特定的應用顯示一個ANR:5秒內對輸入事件無響應。一個BroadCastReceiver在10秒內沒有執行完畢。怎樣避免ANR?考慮到上面對ANR的定義,讓我們來研究一下這是為什么會發生以及怎樣最好的組織你的應用以避免ANR。Android應用正常是運行在一個單獨的(如main)線程中的,這就意味著在你應用主線程中正在做的需要花很長時間來完成的事情都能夠激活ANR對話框。因為你的應用并沒有給自己一個機會來處理輸入事件或Intent廣播。因此任何運行在主線程中的方法應該做盡可能少的事情。特別地Activitiy在關鍵生命周期方法中如onCreate()和onResume()應當做盡可能少的設置。潛在地的耗時長的操作(如網絡或數據庫操作,或高耗費數學計算如改變位圖大小)應該在子線程里面完成(或以數據庫操作為例,可以通過異步請求)。盡管如此,這并不是說當等待子線程完成的過程中你的主線程必須被阻塞你不必調用Thread.wait()或Thread.sleep(),恰恰相反,你的主線程應該為子線程提供一個Handler,以便子線程完成時可以提交回給主線程。以這種方式來設計你的應用,將會允許你的主線程一直可以響應輸入,以避免由5秒鐘的輸入事件超時導致的ANR對話。這些做法同樣應該被其它任何顯示UI的線程所效仿,因為它們屬于同樣類型的超時。IntentReciever執行時間的特定限制限制了它們應該做什么:在后臺執行的一些瑣碎的工作如保存設置或注冊通知。至于其它在主線程里被調用的方法,在BroadcastReceiver中,應用應該避免潛在的長耗時操作或計算,而是應該用子線程來完成密集任務(因為BroadcastReceiver的生命周期是短暫的)。對Intent broadcast作出響應,你的應用應該啟動一個Service來執行長耗時的動作。同樣,你也應該避免從Intent Receiver中啟動Activity,因為它會產生一個新的屏,偷走任何用戶正在運行的應用的焦點。對Intent broadcast作出的響應,假如你的應用需要向用戶顯示什么東西,應該用Notification Manager來完成。增強響應靈敏性通常,在一個應用中,100到200微秒是一個讓用戶感覺到阻滯的閾值,因此這里有些小技巧讓你用來使你的應用看起來響應更靈敏。如果你的應用正在后臺對用戶輸入作出響應,顯示正在進行的進度(ProgressBar和ProgressDialog對此很有用)。特別是對于游戲,在子線程中做移動的計算。如果你的應用有一個耗時的初始化過程,考慮用閃屏或盡可能快地渲染主界面并異步地填充信息。在這兩種情況下你都應該表明進度正在進行,以免用戶覺得你的應用被凍住了。為無縫設計:即使你的應用是快速且響應靈敏的,一些設計仍然能句對用戶造成問題因為與其它應用未計劃的交互或者對話,意外的數據丟失,無意識的阻塞等等。為了避免這些問題,有助于理解你的應用運行的環境和可以影響你的應用的系統交互。總之,你應該倔盡全力地開發一個與系統和其它應用無縫交互的應用。一個常見的無縫問題就是一個應用的后臺進程(如service或broadcast receiver)對某事件作出響應而彈出對話框,這看起來仿佛并無大礙,特別是當你在模擬器上單獨地構建和測試你的應用時。然而,當你的應用在真正的設備上運行,后臺線程顯示對話框時,你的應用當時可能沒有獲得用戶焦點。這就會出現你的應用會在活動的應用后面顯示對話框,或者從當前應用中獲得焦點并顯示對話框的情況,而管論當時用戶正在做什么(如正在打電話等)。那樣的行為可能對你的應用或用戶不起作用。為了避免這些問題,你的應用應該利用適當的系統資源Notification類,來通知用戶。利用通知,你的應用可以通過在狀態條上顯示一個圖標來通知用戶事件已經發生,而非獲得焦點和打斷用戶。另外一個無縫問題的例子就是,Activity由于未能正確實現onPause()及其它生命周期方法而無意中丟失了狀態或用戶數據。又或者,你的應用要暴露供其它應用使用的數據,你應該通過ContentProvider來實現,而非通過一個全世界都可讀的原始文件或數據庫。這些例子的共同特點就是,它們都是關于如何跟系統和其它應用協作得更好,Android系統的設計就是將所有的應用看作是一個松散耦合的組件聯邦,不是一堆墨盒代碼。這就使你作為一個開發者可以將整個系統視為只是一個更大一點的組件聯邦。這樣有得于你將應用與其它應用清晰和無縫的集成,所以作為回報,你應該更好的設計你的代碼。這個文檔討論了常見的無縫集成問題和怎樣去避免它們。它包含了如下主題:別丟棄數據一定要記住Android是一個移動平臺。看起來很顯然,但是記住這個事實很重要,就是任何Activity(如”正在打進來的電話”應用)在任何時候都有可能彈出來覆蓋你的Activity,這會激活onSaveInstanceState()和onPause()方法,并導致你的應用被殺死。如果當其它Activity出現時,用戶正好在你的應用上編輯數據,你的應用被殺死的同時那些數據也很可能會丟失。當然了,除非你先保存了進行中的工作。“Android方式”是這么做的:能接收和編輯用戶輸入的應用需要重寫onSaveInstanceState()方法并以恰當的方式保存它們的狀態,當用戶重新訪問應用時,就能重新獲得數據。一個運用這個行為經典的例子就是郵件應用,當用戶正在撰寫郵件時另一人Activity開始了,應用應該將正在編輯的郵件保存為草稿。不要暴露原始數據如果你不想穿著內衣在大街上散步,同樣你的數據也不應如此。盡管可能暴露某些應用可以方便其它應用讀取,但這通常不是最好的主意。暴露原始數據要求其它的應用能夠理解你的數據格式;如果你改變了格式,你將會破壞其它沒有同時更新的應用。“Android方式”就是創建一個ContentProvider通過一個清晰的、深思熟慮的、可維護的API來暴露你的數據給其它應用。使用ContentProvider就像一個Java接口來分離和組件化兩段緊密耦合的代碼,這就意味著你能夠修改你數據的內部格式而不用修改由ContentProvider暴露的接口,這樣就不會影響其它應用。別打斷用戶如果能確定一個用戶是帶有目的性的運行一個應用才是安全的。那就是為什么你除非是直接響應當前活動的用戶輸入,不然就要避免產生Activity的原因。那就是說,不要從后臺運行的BroadcastReceiver和Service中調用startActivity()。如果這樣做將會打斷任何正在運行的應用,并使用戶惱怒。甚至你的Activity可能成為一個“擊鍵強盜”接收一些用戶正在為上一個Activity提供的輸入,視乎你的應用所做的,這是這可能是個壞消息。取代直接從后臺直接產生Activity UIs,你應該用NotificationManager來設置通知,這將會出現在狀態條上,當用戶空閑時可以點擊它們來看你的應用向他們顯示了什么。(注意,當你的Activity已經在前臺時所有這些都沒適用:這時,對于輸入的響應,用戶期望看到你的下一個Activity。)有太多事要做?在一個線程里做如果你的應用需要做一個代價高昂或長耗時的計算,你可能要將它移到一個線程里。這個將會防止顯示“Application Not Responding”對話框給用戶,最終導致你的應用完全終止。默認地,在一個Activity中的代碼和其所有的View運行在同一個線程上。這與處理UI事件的線程是同一個。例如,當一個鍵被按下時,一個key-down事件被添加到Activity主線程的隊列。事件處理系統需要很快地讓這個事件出列并處理這個事件。不然,系統數秒后將會認為應用已經掛起并替用戶殺死這個應用。如果你有長耗時的代碼,讓它在你的Activity上內聯運行將會在使它運行在事件處理線程上,這很大程度上阻塞了了事件處理句柄。這會延緩輸入處理并導致ANR對話框。為了避免之,將你的計算移到一個線程中。在為響應靈敏性設計中已經討論了如何做。5)不要過載一個單一的Activity屏任何值得使用的應用都可能會有幾個不同的屏幕。當設計你的UI屏幕時,請一定要運用多個Activity對象實例。依賴于你的開發背景,你可能像解釋某些類似Java Applet的東西一樣來解釋一個Activity,Activity是你應用的入口點。然而,那并不是準確:一個pplet的子類是一個Javapplet的單一入口點,而一個Activity應該被看作一個潛在的進入你的應用的多個入口點。在你的”main”Activity和任何其它你可能有的Activity之間的唯一不同就是,那“miain”Activity碰巧是那個唯一在你的AndroidManifest.xml文件中對“ent.action.MAIN”動作有興趣的一個而已。所以,當設計你的應用時,把你的應用看成一個Activity對象的聯邦。從長遠來看,這會使得你的代碼更具可維護性。6)擴展系統主題當提到用戶接口的觀感時,協調是很重要的。
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 保定幼兒師范高等專科學校《影視項目管理》2023-2024學年第二學期期末試卷
- 蘭州信息科技學院《燈光與建聲設計》2023-2024學年第二學期期末試卷
- 黃山學院《教師口語(普通話)》2023-2024學年第二學期期末試卷
- 長江工程職業技術學院《班主任工作技能》2023-2024學年第二學期期末試卷
- 浙江水利水電學院《課件制作》2023-2024學年第二學期期末試卷
- 鎮江市高等專科學校《中學語文教材分析與教學設計》2023-2024學年第二學期期末試卷
- 浙江電力職業技術學院《現代服務業管理》2023-2024學年第二學期期末試卷
- 廣東東軟學院《機械專業學位類別論文寫作指導》2023-2024學年第二學期期末試卷
- 教師與學生的溝通
- 山西工程科技職業大學《材料成型設備及其自動化》2023-2024學年第二學期期末試卷
- 廣西地方公路養護工程預算定額
- 2025年第三屆天揚杯建筑業財稅知識競賽題庫附答案(501-1000題)
- 《中式美食鑒賞》課件
- 姐妹間房屋轉讓合同協議
- 國開電大軟件工程形考作業3參考答案 (一)
- 2025年11.0C-雙基地感知關鍵技術研究與驗證白皮書-未來移動通信論壇
- 刑事退賠和解協議書
- 《天津T建設集團公司應收賬款管理問題及完善對策研究》9800字(論文)
- 盆腔器官脫垂診療規范與指南
- 箱式變電站及相關配套電力設施項目可行性研究報告申請報告套用
- 輸血病人的個案護理
評論
0/150
提交評論