一:學會懶惰
沒搞錯吧?竟然讓程序開發人員學會懶惰?程序開發人員可能是世界上最為忙碌的一類人啦!對,沒錯,學會懶惰!正因為程序開發人員忙碌,正因為程序開發人員可能會在客戶無限變化的需求之下沒日沒夜的加班,所以要學會懶惰,這樣,你就可以把更多的時間浪費在美好的事物身上!
如何懶惰:
1,Don’t?Reinvent?the?Wheel(不要重復發明輪子)。
2,Inventing?the?Wheel(發明輪子)。
1 ,Don’t?Reinvent?the?Wheel(不要重復發明輪子)。
“輪子理論”,也即“不要重復發明輪子”,這是西方國家的一句諺語,原話是:Don’t?Reinvent?the?Wheel。“不要重復發明輪子?”意思是企業中任何一項工作實際上都有人做過,我們所需要做的就是找到做過這件事情的人。拿到軟件領域中就是指有的項目或功能,別人已經做過,我們需要用的時候,直接拿來用即可,而不要重新制造。
Android號稱是首個為移動終端打造的真正開放和完整的移動軟件。Android發布后不久Google公司就發布了操作系統核心(Kernel)與部分驅動程序的源代碼,到目前位置除了Google?Map等Google公司的核心組件沒有開放源代碼外,Android基本完成了完全的開源,這就極大的促進了Android的普及和移植。受到Android開放行為和開源精神的影響,在世界各地,有成千上萬的程序員喜歡和別人分享自己的聰明才智和自己編寫的代碼。你可以在Google的Android討論組或者Google搜索引擎上搜索到很多優秀的程序代碼。這樣做并不是鼓勵大家整天等著讓別人為你編寫代碼,而是你可以“站在偉人的肩膀上”,充分發揚“拿來主義”,聰明地應用別人的程序代碼可以節省你大量的時間。
下面筆者為大家介紹幾個通用的類,這些類來自筆者平日的收集,如果你能把它們加入到你自己的類庫中,遲早你會發現自己在進行Android開發的時候受益無窮:
2 ,Inventing?the?Wheel(發明輪子)。
發明輪子?不錯,發明輪子!我們不僅要發明輪子,更要成為努力成為世界上發明輪子的主導力量,唯有這樣,才能談的上中華名族軟件大業的真正強大。在Android,要發明輪子,就是我們要主動的是解決一些世界上他人未解決的難題或者創造新的編程框架或者對Android進行深度的改造以適合自己的業務發展需要。Google發布了Android后不久,中國移動便投入了大量的人力和物力,在Android的基礎上創建融入自己業務并開發、封裝了新的功能的和框架的OMS,這是Android中發明輪子的一個非常重要的例子。可能你會說,這發明輪子也太難了吧,別急,我們慢慢來,開發一個框架特定領域的框架吧!你可能會一臉無辜的說,開發一個框架是說的那么容易嗎?當然不是啦。但是也并非不可能,首先,我們分析一下框架的魅力的源泉,看看Spring、Struts等Java?EE框架,在看看.NET框架,當然也可以看看發展的如火如荼、層出不窮的PHP框架,她們的強大和魅力的源泉都在于:IoC(Inversion?of?Control)。
Don’t?call?us,?we’ll?call?you(別找我,我會來找你的)。我們下面就自己發明一個輪子的模型,實際展示一個框架最初核心的類,讓你一飽眼福:
二:精通Android體系架構、MVC、常見的設計模式、控制反轉(IoC)
1,請看某個著名的IT公司一則招聘信息的其中一條要求:“熟悉Android系統架構及相關技術,1年以上實際Android平臺開發經驗;”,里面非常明確的說道要求熟練Android系統架構,這從某種程度上說明了對Android體系架構的理解的重要性,下面我們看看Android體系結構圖,該圖源自Android的文檔:
很明顯,上圖包含四個主要的層次:
Linux?Kernel:負責硬件的驅動程序、網絡、電源、系統安全以及內存管理等功能。
Libraries和Android?Runtime:Libraries:即C/C++函數庫部分,大多數都是開放源代碼的函數庫,例如WebKit,該函數庫負責Android網頁瀏覽器的運行,例如標準的C函數庫Libc、OpenSSL、SQLite等,當然也包括支持游戲開發2D?SGL和3D?OpenGL?|?ES,在多媒體方面有MediaFramework框架來支持各種影音和圖形文件的播放與顯示,例如MPEG4、H.264、MP3、AAC、AMR、JPG和PNG等眾多的多媒體文件格式。Android的Runtime負責解釋和執行生成的Dalvik格式的字節碼。
Application?Framework(應用軟件架構),Java應用程序開發人員主要是使用該層封裝好的API進行快速開發。
Applications:該層是Java的應用程序層,Android內置的Google?Maps、E-mail、即時通信工具、瀏覽器、MP3播放器等處于該層,Java開發人員開發的程序也處于該層,而且和內置的應用程序具有平等的位置,可以調用內置的應用程序,也可以替換內置的應用程序。
上面的四個層次,下層為上層服務,上層需要下層的支持,調用下層的服務,這種嚴格分層的方式帶來的極大的穩定性、靈活性和可擴展性,使得不同層的開發人員可以按照規范專心特定層的開發。
Android應用程序使用框架的API并在框架下運行,這就帶來了程序開發的高度一致性,另一方面也告訴我們,要想寫出優質高效的程序就必須對整個Application?Framework進行非常深入的理解。精通Application?Framework,你就可以真正的理解Android的設計和運行機制,也就更能夠駕馭整個應用層的開發。
2 ,Android的官方建議應用程序的開發采用MVC模式。何謂MVC?先看看下圖
MVC是Model,View,Controller的縮寫,從上圖可以看出MVC包含三個部分:
1.模型(Model)對象:是應用程序的主體部分,所有的業務邏輯都應該寫在該層。
2.視圖(View)對象:是應用程序中負責生成用戶界面的部分。也是在整個MVC架構中用戶唯一可以看到的一層,接收用戶的輸入,顯示處理結果。
3.控制器(Control)對象:是根據用戶的輸入,控制用戶界面數據顯示及更新Model對象狀態的部分,控制器更重要的一種導航功能,想用用戶出發的相關事件,交給M哦得了處理。
Android鼓勵弱耦合和組件的重用,在Android中MVC的具體體現如下:
1)?視圖層(View):一般采用XML文件進行界面的描述,使用的時候可以非常方便的引入,當然,如何你對Android了解的比較的多了話,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作為View層,當然這里需要進行Java和JavaScript之間的通信,幸運的是,Android提供了它們之間非常方便的通信實現。
2)?控制層(Controller):Android的控制層的重任通常落在了眾多的Acitvity的肩上,這句話也就暗含了不要在Acitivity中寫代碼,要通過Activity交割Model業務邏輯層處理,這樣做的另外一個原因是Android中的Acitivity的響應時間是5s,如果耗時的操作放在這里,程序就很容易被回收掉。
3)?模型層(Model):對數據庫的操作、對網絡等的操作都應該在Model里面處理,當然對業務計算等操作也是必須放在的該層的。
3 ,設計模式和IoC(控制反轉)
毫無疑問,Android的之所以能夠成為一個開放的氣象萬千的系統,與設計模式的精妙應用是分不開的,只要你稍微用心觀察,就會發現在Android中到處都是A設計模式或者設計模式的聯合運用,以下的設計模式是您游刃有余的駕馭Android所必須掌握的:
1.? Template?Method模式 :(模板模式)
定義一個操作中算法的骨架,而將一些步驟延遲到子類中。不改變算法的結構而重新定義它的步驟。
2.? Factory?Method模式:(工廠模式)
FactoryMethod是一種創建性模式,它定義了一個創建對象的接口,但是卻讓子類來決定具體實例化哪一個類.當一個類無法預料要創建哪種類的對象或是一個類需要由子類來指定創建的對象時我們就需要用到Factory Method 模式了.簡單說來,Factory Method可以根據不同的條件產生不同的實例,當然這些不同的實例通常是屬于相同的類型,具有共同的父 類.Factory Method把創建這些實例的具體過程封裝起來了,簡化了客戶端的應用,也改善了程序的擴展性,使得將來可以做最小的改動就可以加入新的待創建的類. 通常我們將Factory Method作為一種標準的創建對象的方法,當發現需要更多的靈活性的時候,就開始考慮向其它創建型模式轉化
3.? Observer模式:(觀測者模式)
observer模式定義對象間的一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。JDK里提供的observer設計模式的實現由java.util.Observable類和java.util.Observer接口組成。從名字上可以清楚的看出兩者在Observer?設計模式中分別扮演的角色:Observer是觀察者角色,Observable是被觀察目標(subject)角色。
4.? Abstract?Factory模式:(抽象工廠模式)
為創建一組相關或相互依賴的對象提供一個接口,而且無需指定它們的具體類。大致意思是說:我們在創建這些對象的時候,并不需要指定它們的具體類,這些具體類的對象是由工廠對象負責實例化的。
5.? Adapter模式:(適配器模式)
把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
6. Composite模式:(合成模式)
定義: 將對象組合成樹形結構以表示“整體—部分”的層次結構。Composite模式使單個對象和組合對象的使用具有一致性。
合成模式將對象組織到樹結構中,可以用來描述整體和部分的關系。合成模式可以使客戶端將單純的元素和復合的元素同等看待。
注意:用文件系統來理解合成模式可以說是個很好的方式。
7.? Strategy模式:(策略模式)
定義一系列算法,把他們封裝起來,并可相互替換,使算法可獨立于使用他的客戶而變化。
8.? State模式:(聲明模式)
定義一系列算法,把他們封裝起來,并可相互替換,使算法可獨立于使用他的客戶而變化。
State模式主要解決的是在開發中時常遇到的根據不同的狀態需要進行不同的處理操作的問題,而這樣的問題,大部分人是采用switch-case語句進行處理的,這樣會造成一個問題:分支過多,而且如果加入一個新的狀態就需要對原來的代碼進行編譯。State模式采用了對這些不同的狀態進行封裝的方式處理這類問題,當狀態改變的時候進行處理然后再切換到另一種狀態,也就是說把狀態的切換責任交給了具體的狀態類去負責.同時,State模式和Strategy模式有很多相似的地方,需要說明的是兩者的思想都是一致的,只不過封裝的東西不同:State模式封裝的是不同的狀態,而Stategy模式封裝的是不同的算法。
9.? Proxy模式:(代理模式)
定義為其他對象提供一種代理以控制這個對象的訪問。
Proxy代理模式是一種結構型設計模式,主要解決的問題是:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層,這個訪問層也叫代理。Proxy模式是最常見的模式,在我們生活中處處可見,例如我們買火車票不一定非要到火車站去買,可以到一些火車票的代售點去買。寄信不一定是自己去寄,可以把信委托給郵局,由郵局把信送到目的地,現實生活中還有很多這樣的例子,就不一一列舉了。
10. Bridge模式:(橋梁模式)
定義 : 將抽象和行為劃分開來,各自獨立,但能動態的結合.
橋梁模式的用意是 “將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化”。這句話有三個關鍵詞,也就是抽象化、實現化和脫耦。
11. Iterator模式:(迭代器模式)
定義:提供一個方法順序訪問一個聚合對象的各個元素,而又不暴露該對象的內部表示。
這個模式在java的類庫中已經實現了,在java中所有的集合類都實現了Conllection接口,而Conllection接口又繼承了Iterable接口,該接口有一個iterator方法,也就是所以的集合類都可以通過這個iterator方法來轉換成Iterator類,用Iterator對象中的hasnext方法來判斷是否還有下個元素,next方法來順序獲取集合類中的對象。
12. Memento 模式:(備忘錄模式)
定義:在不破壞封裝的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣就可以將該對象恢復到原先保存前的狀態。
在程序運行過程中,某些對象的狀態處在轉換過程中,可能由于某種原因需要保存此時對象的狀態,以便程序運行到某個特定階段,需要恢復到對象之前處于某個點時的狀態。如果使用一些公有接口讓其它對象來得到對象的狀態,便會暴露對象的實現細節。
13. Facade模式:(外觀模式)
定義:為子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
Facade外觀模式,是一種結構型模式,它主要解決的問題是:組件的客戶和組件中各種復雜的子系統有了過多的耦合,隨著外部客戶程序和各子系統的演化,這種過多的耦合面臨很多變化的挑戰。在這里我想舉一個例子:比如,現在有一輛汽車,我們(客戶程序)要啟動它,那我們就要發動引擎(子系統1),使四個車輪(子系統2)轉動。但是實際中我們并不需要用手推動車輪使其轉動,我們踩下油門,此時汽車再根據一些其他的操作使車輪轉動。油門就好比系統給我們留下的接口,不論汽車是以何種方式轉動車輪,車輪變化成什么牌子的,我們要開走汽車所要做的還是踩下油門。
14. Mediator 模式: (中介者模式)
定義:用一個中介者對象來封裝一系列的對象交互。中介者使各對象不需要顯式的相互引用,從而使其耦合松散,而且可以獨立的改變他們之間的交互。
15. Chain of Responsibility模式 : (責任鏈模式)
定義: 為了避免請求的發送者和接收者之間的耦合關系,使多個接受對象都有機會處理請求。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
Struts2、tomcat的過濾器采用的模式(過濾器是它們的核心機制)
16. Command模式 : (命令模式)
定義:將一個請求封裝為一個對象,從而使你不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤銷的操作。
Commad模式是一種對象行為模式,它可以對發送者(sender)和接收者(receiver)完全解耦(decoupling)。(”發送者” 是請求操作的對象,”接收者” 是接收請求并執行某操作的對象。有了 “解耦”,發送者對接收者的接口一無所知。)這里,”請求”(request)這個術語指的是要被執行的命令。Command模式還讓我們可以對 “何時” 以及 “如何” 完成請求進行改變。因此,Command模式為我們提供了靈活性和可擴展性。
17. Builder模式 : (建造者模式)
定義:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
建造模式是一步一步創建一個復雜的對象,它允許用戶可以只通過指定復雜對象的類型和內容就可以構建它們,用戶不知道內部的具體構建細節。
18. Prototype模式 (原型模式)
通過給出一個原型對象來指明所要創建的對象類型,然后用復制這個原型對象的辦法創建出更多的同類型對象。
在java的類庫中已經實現了這一模式,只要你定義的類實現了Cloneable接口,用這個類所創建的對象可以做為原型對象進而克隆出更多的同類型的對象。
19. Singleton 模式: ?(單例模式) —–最簡單的模式
定義:保證一個類只有一個實例,并提供一個訪問它的全局訪問點。
20. Visitor模式 : (訪問者模式)
定義:表示一個作用于某對象結構中各元素的操作。它可以使你不修改各元素類的前提下定義作用于這些元素的新操作,也就是動態的增加新的方法。
21. FlyWeight模式:(享元模式)
定義:運用共享技術有效地支持大量細粒度對象。
也就是說在一個系統中如果有多個相同的對象,那么只共享一份就可以了,不必每個都去實例化一個對象。在Flyweight模式中,由于要產生各種各樣的對象,所以在Flyweight(享元)模式中常出現Factory模式。Flyweight的內部狀態是用來共享的,Flyweight factory負責維護一個對象存儲池(Flyweight Pool)來存放內部狀態的對象。Flyweight模式是一個提高程序效率和性能的模式,會大大加快程序的運行速度。
22.Decorator 模式: (裝飾模式)
定義:動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式比生成子類更為靈活。
也就是說:動態地給對象添加一些額外的功能。它的工作原理是:創建一個始于Decorator對象(負責新功能的對象)終止于原對象的一個對象的“鏈”。例如,我們要為超市的收銀臺設計一個打印票據的程序,有的需要打印票據的頭信息,有的需要打印票據的頁腳信息,有的只需要打印票據的內容。如果針對每一種情況都修改一次程序,勢必會很麻煩。這時我們可以考慮使用Decorator模式。
23. Ioc 模式:(控制反轉模式)
IoC是一種用來解決組件(實際上也可以是簡單的Java類)之間依賴關系、配置及生命周期的設計模式,其中對組件依賴關系的處理是IoC的精華部分。IoC的實際意義就是把組件之間的依賴關系提取(反轉)出來,由容器來具體配置。這樣,各個組件之間就不存在hard-code的關聯,任何組件都可以最大程度的得到重用。運用了IoC模式后我們不再需要自己管理組件之間的依賴關系,只需要聲明由容器去實現這種依賴關系。就好像把對組件之間依賴關系的控制進行了倒置,不再由組件自己來建立這種依賴關系而交給容器(例如PicoContainer、Spring)去管理。
Android框架魅力的源泉在于IoC,在開發Android的過程中你會時刻感受到IoC帶來的巨大方便,就拿Activity來說,下面的函數是框架調用自動調用的:
protected?void?onCreate(Bundle?savedInstanceState)?;
不是程序編寫者主動去調用,反而是用戶寫的代碼被框架調用,這也就反轉了!當然IoC本身的內涵遠遠不止這些,但是從這個例子中也可以窺視出IoC帶來的巨大好處。此類的例子在Android隨處可見,例如說數據庫的管理類,例如說Android中SAX的Handler的調用等。有時候,您甚至需要自己編寫簡單的IoC實現,上面展示的多線程現在就是一個說明。
三:編寫可重用、可擴展、可維護、靈活性高的代碼
Android應用程序的開發是使用Java編寫,在架構上使用MVC,鼓勵組件之間的若耦合。開發出編寫可重用、可擴展、可維護、靈活性高的代碼需要經歷遵循以下原則:
l?” 開-閉”原則(OCP): 一個軟件實體應當對擴展開放,對修改關閉。這個原則說的是,在設計一個模塊的時候,應當使這個模塊可以在不被修改的前提下被擴展。換言之,應當可以在不必修改源代碼的情況下改變這個模塊的行為。
l 里氏代換原則(LSP) :一個軟件實體如果使用的是一個基類的話,那么一定使用于其子類,而且它根本不能察覺出基類對象和子類對象的區別。
l?依賴倒轉原則(DIP):要依賴于抽象,不要依賴于具體。
l?接口隔離原則(ISP):使用多個專門的接口比使用單一的總接口要好。一個類對另外一個類的依賴性應當是建立在最小的接口上的。
l?合成/聚合復用原則(CARP):又稱合成復用原則(CRP),就是在一個新的對象里面使用一些已有的對象,使之成為新對象的一部分;新的對象通過向這些對象的委派達到復用已有功能的目的。簡而言之就是:要盡量使用合成/聚合,盡量不要使用繼承。
l?迪米特法則(LoD):又稱最少知識原則(LKP),就是說一個對象應當對其他對象盡可能少的了解。狹義的迪米特法則是指如果兩個類不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用.如果其中一個類需要調用另一個類的方法的話,可以通過第三者轉發這個調用.。廣義的迪米特法則是指一個模塊設計得好壞的一個重要的標志就是該模塊在多大的程度上將自己的內部數據與實現有關的細節隱藏起來。信息的隱藏非常重要的原因在于,它可以使各個子系統之間脫耦,從而允許它們獨立地被開發,優化,使用閱讀以及修改.。
靈活的使用設計模式可以在面對千變萬化的業務需求是編寫出可重用、可擴展、可維護、靈活性高的代碼。
當然,由于Android是運行在移動設備上的,而移動設備的處理能力是有限的,所以有時間必須在編寫可重用、可擴展、可維護、靈活性高的代碼與高效的代碼之間做出適當的平衡。
四:高效的編寫高效的代碼
高效快速的編寫代碼和編寫高效率執行的代碼很多時候都是對立的死敵,很多時候,你想快速的開發,代碼的執行效率往往就會慢下來;你想編寫高效的代碼,開發速度就會慢下來。
不重復發明輪子和發明新的輪子是高效的編寫高效的代碼的正確是道路。
關于高效的代碼,下面網絡的一篇文章,直接轉載(不知道是哪位哥們寫的)如下:
“現代的手持設備,與其說是電話,更像一臺拿在手中的電腦。但是,即使是“最快”的手持設備,其性能也趕不上一臺普通的臺式電腦。
這就是為什么我們在書寫Android應用程序的時候要格外關注效率。這些設備并沒有那么快,并且受電池電量的制約。這意味著,設備沒有更多的能力,我們必須把程序寫的盡量有效。
本文討論了很多能讓開發者使他們的程序運行更有效的方法,遵照這些方法,你可以使你的程序發揮最大的效力。
對于占用資源的系統,有兩條基本原則:
1.?不要做不必要的事
2.?不要分配不必要的內存
所有下面的內容都遵照這兩個原則。
有些人可能馬上會跳出來,把本節的大部分內容歸于“草率的優化”(xing:參見[The?Root?of?All?Evil]),不可否認微優化(micro-optimization。xing:代碼優化,相對于結構優化)的確會帶來很多問題,諸如無法使用更有效的數據結構和算法。但是在手持設備上,你別無選擇。假如你認為Android虛擬機的性能與臺式機相當,你的程序很有可能一開始就占用了系統的全部內存(xing:內存很小),這會讓你的程序慢得像蝸牛一樣,更遑論做其他的操作了。
Android的成功依賴于你的程序提供的用戶體驗。而這種用戶體驗,部分依賴于你的程序是響應快速而靈活的,還是響應緩慢而僵化的。因為所有的程序都運行在同一個設備之上,都在一起,這就如果在同一條路上行駛的汽車。而這篇文檔就相當于你在取得駕照之前必須要學習的交通規則。如果大家都按照這些規則去做,駕駛就會很順暢,但是如果你不這樣做,你可能會車毀人亡。這就是為什么這些原則十分重要。
當我們開門見山、直擊主題之前,還必須要提醒大家一點:不管VM是否支持實時(JIT)編譯器(xing:它允許實時地將Java解釋型程序自動編譯成本機機器語言,以使程序執行的速度更快。有些JVM包含JIT編譯器。),下面提到的這些原則都是成立的。假如我們有目標完全相同的兩個方法,在解釋執行時foo()比bar()快,那么編譯之后,foo()依然會比bar()快。所以不要寄希望于編譯器可以拯救你的程序。
避免建立對象
世界上沒有免費的對象。雖然GC為每個線程都建立了臨時對象池,可以使創建對象的代價變得小一些,但是分配內存永遠都比不分配內存的代價大。
如果你在用戶界面循環中分配對象內存,就會引發周期性的垃圾回收,用戶就會覺得界面像打嗝一樣一頓一頓的。
所以,除非必要,應盡量避免盡力對象的實例。下面的例子將幫助你理解這條原則:
當你從用戶輸入的數據中截取一段字符串時,盡量使用substring函數取得原始數據的一個子串,而不是為子串另外建立一份拷貝。這樣你就有一個新的String對象,它與原始數據共享一個char數組。
如果你有一個函數返回一個String對象,而你確切的知道這個字符串會被附加到一個StringBuffer,那么,請改變這個函數的參數和實現方式,直接把結果附加到StringBuffer中,而不要再建立一個短命的臨時對象。
一個更極端的例子是,把多維數組分成多個一維數組。
int數組比Integer數組好,這也概括了一個基本事實,兩個平行的int數組比(int,int)對象數組性能要好很多。同理,這試用于所有基本類型的組合。
如果你想用一種容器存儲(Foo,Bar)元組,嘗試使用兩個單獨的Foo[]數組和Bar[]數組,一定比(Foo,Bar)數組效率更高。(也有例外的情況,就是當你建立一個API,讓別人調用它的時候。這時候你要注重對API借口的設計而犧牲一點兒速度。當然在API的內部,你仍要盡可能的提高代碼的效率)
總體來說,就是避免創建短命的臨時對象。減少對象的創建就能減少垃圾收集,進而減少對用戶體驗的影響。
使用本地方法
當你在處理字串的時候,不要吝惜使用String.indexOf(),?String.lastIndexOf()等特殊實現的方法(specialty?methods)。這些方法都是使用C/C++實現的,比起Java循環快10到100倍。
使用實類比接口好
假設你有一個HashMap對象,你可以將它聲明為HashMap或者Map:
Map?myMap1?=?new?HashMap();
HashMap?myMap2?=?new?HashMap();
哪個更好呢?
按照傳統的觀點Map會更好些,因為這樣你可以改變他的具體實現類,只要這個類繼承自Map接口。傳統的觀點對于傳統的程序是正確的,但是它并不適合嵌入式系統。調用一個接口的引用會比調用實體類的引用多花費一倍的時間。
如果HashMap完全適合你的程序,那么使用Map就沒有什么價值。如果有些地方你不能確定,先避免使用Map,剩下的交給IDE提供的重構功能好了。(當然公共API是一個例外:一個好的API常常會犧牲一些性能)
用靜態方法比虛方法好
如果你不需要訪問一個對象的成員變量,那么請把方法聲明成static。虛方法執行的更快,因為它可以被直接調用而不需要一個虛函數表。另外你也可以通過聲明體現出這個函數的調用不會改變對象的狀態。不用getter和setter
在很多本地語言如C++中,都會使用getter(比如:i?=?getCount())來避免直接訪問成員變量(i?=?mCount)。在C++中這是一個非常好的習慣,因為編譯器能夠內聯訪問,如果你需要約束或調試變量,你可以在任何時候添加代碼。
在Android上,這就不是個好主意了。虛方法的開銷比直接訪問成員變量大得多。在通用的接口定義中,可以依照OO的方式定義getters和setters,但是在一般的類中,你應該直接訪問變量。
將成員變量緩存到本地
訪問成員變量比訪問本地變量慢得多,下面一段代碼:
for?(int?i?=?0;?i?<?this.mCount;?i++)
dumpItem(this.mItems[i]);
最好改成這樣:
int?count?=?this.mCount;
Item[]?items?=?this.mItems;
for?(int?i?=?0;?i?<?count;?i++)
dumpItems(items[i]);
(使用”this”是為了表明這些是成員變量)
另一個相似的原則是:永遠不要在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())?{
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);
}
}
這里有4次訪問成員變量mScrollBar,如果將它緩存到本地,4次成員變量訪問就會變成4次效率更高的棧變量訪問。
另外就是方法的參數與本地變量的效率相同。
使用常量
讓我們來看看這兩段在類前面的聲明:
static?int?intVal?=?42;
static?String?strVal?=?”Hello,?world!”;
必以其會生成一個叫做<clinit>的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將42賦給intVal,然后把一個指向類中常量表的引用賦給strVal。當以后要用到這些值的時候,會在成員變量表中查找到他們。
下面我們做些改進,使用“final”關鍵字:
static?final?int?intVal?=?42;
static?final?String?strVal?=?”Hello,?world!”;
現在,類不再需要<clinit>方法,因為在成員變量初始化的時候,會將常量直接保存到類文件中。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字符串常量,而不是使用成員變量。
將一個方法或類聲明為”final”不會帶來性能的提升,但是會幫助編譯器優化代碼。舉例說,如果編譯器知道一個”getter”方法不會被重載,那么編譯器會對其采用內聯調用。
你也可以將本地變量聲明為”final”,同樣,這也不會帶來性能的提升。使用”final”只能使本地變量看起來更清晰些(但是也有些時候這是必須的,比如在使用匿名內部類的時候)(xing:原文是?or?you?have?to,?e.g.?for?use?in?an?anonymous?inner?class)
謹慎使用foreach
foreach可以用在實現了Iterable接口的集合類型上。foreach會給這些對象分配一個iterator,然后調用?hasNext()和next()方法。你最好使用foreach處理ArrayList對象,但是對其他集合對象,foreach相當于使用?iterator。
下面展示了foreach一種可接受的用法:
public?class?Foo?{
int?mSplat;
static?Foo?mArray[]?=?new?Foo[27];
public?static?void?zero()?{
int?sum?=?0;
for?(int?i?=?0;?i?<?mArray.length;?i++)?{
sum?+=?mArray[i].mSplat;
}
}
public?static?void?one()?{
int?sum?=?0;
Foo[]?localArray?=?mArray;
int?len?=?localArray.length;
for?(int?i?=?0;?i?<?len;?i++)?{
sum?+=?localArray[i].mSplat;
}
}
public?static?void?two()?{
int?sum?=?0;
for?(Foo?a:?mArray)?{
sum?+=?a.mSplat;
}
}
}
在zero()中,每次循環都會訪問兩次靜態成員變量,取得一次數組的長度。
retrieves?the?static?field?twice?and?gets?the?array?length?once?for?every?iteration?through?the?loop.
在one()中,將所有成員變量存儲到本地變量。
pulls?everything?out?into?local?variables,?avoiding?the?lookups.
two()使用了在java1.5中引入的foreach語法。編譯器會將對數組的引用和數組的長度保存到本地變量中,這對訪問數組元素非常好。但是編譯器還會在每次循環中產生一個額外的對本地變量的存儲操作(對變量a的存取)這樣會比one()多出4個字節,速度要稍微慢一些。
綜上所述:foreach語法在運用于array時性能很好,但是運用于其他集合對象時要小心,因為它會產生額外的對象。
避免使用枚舉
枚舉變量非常方便,但不幸的是它會犧牲執行的速度和并大幅增加文件體積。例如:
public?class?Foo?{public?enum?Shrubbery?{?GROUND,?CRAWLING,?HANGING?}}
會產生一個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?stuff?1
else?if?(list.items[n].e?==?MyEnum.VAL_Y)//?do?stuff?2
}
替換為:
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++)?{
int?valItem?=?items[n].e.ordinal();
if?(valItem?==?valX)//?do?stuff?1
else?if?(valItem?==?valY)//?do?stuff?2
}
會使性能得到一些改善,但這并不是最終的解決之道。
將與內部類一同使用的變量聲明在包范圍內
請看下面的類定義:
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是一個完全獨立的類,它要直接訪問Foo的私有成員是非法的。要跨越這個鴻溝,編譯器需要生成一組方法:
static?int?Foo.access$100(Foo?foo)?{
return?foo.mValue;
}
static?void?Foo.access$200(Foo?foo,?int?value)?{
foo.doStuff(value);
}
內部類在每次訪問”mValue”和”doStuff”方法時,都會調用這些靜態方法。就是說,上面的代碼說明了一個問題,你是在通過接口方法訪問這些成員變量和函數而不是直接調用它們。在前面我們已經說過,使用接口方法(getter、setter)比直接訪問速度要慢。所以這個例子就是在特定語法下面產生的一個“隱性的”性能障礙。
通過將內部類訪問的變量和函數聲明由私有范圍改為包范圍,我們可以避免這個問題。這樣做可以讓代碼運行更快,并且避免產生額外的靜態方法。(遺憾的是,這些域和方法可以被同一個包內的其他類直接訪問,這與經典的OO原則相違背。因此當你設計公共API的時候應該謹慎使用這條優化原則)
避免使用浮點數
在奔騰CPU出現之前,游戲設計者做得最多的就是整數運算。隨著奔騰的到來,浮點運算處理器成為了CPU內置的特性,浮點和整數配合使用,能夠讓你的游戲運行得更順暢。通常在桌面電腦上,你可以隨意的使用浮點運算。
但是非常遺憾,嵌入式處理器通常沒有支持浮點運算的硬件,所有對”float”和”double”的運算都是通過軟件實現的。一些基本的浮點運算,甚至需要毫秒級的時間才能完成。
甚至是整數,一些芯片有對乘法的硬件支持而缺少對除法的支持。這種情況下,整數的除法和取模運算也是有軟件來完成的。所以當你在使用哈希表或者做大量數學運算時一定要小心謹慎。?”
五,學會至少一門服務器端開發技術
可能有朋友會問:學習Android應用程序開發為什么還需要學習學會至少一門服務器端開發技術呢?答案如下:一方面Android號稱是首個為移動終端打造的真正開放和完整的移動軟件。作為一種移動終端,必須與服務器端結合才能發揮巨大的作用。簡言之,需要:云端+云的方式。Android是為移動互聯網時代量身打造的,移動互聯網時代的服務模式是“手機終端+互聯網絡+應用軟件”,移動互聯網時代應用技術之一的Android只是用于開發移動終端軟件,而服務端技術用于開發互聯網絡應用,所以未來移動互聯網時代軟件的主流應用模式將是“手機客戶端+互聯網絡應用服務端”,這種模式要求做移動互聯網開發的程序員不但要掌握像Android這樣的手機終端軟件技術還要掌握開發互聯網絡應用的服務器端技術。目前,軟件企業普遍存在這樣的問題,做移動互聯網開發Android終端軟件的程序員不了解web應用技術,而做web應用的程序員不了解移動終端技術,這樣就導致了客戶端與服務端在銜接上出現了問題。目前的現狀是:既掌握移動互聯網Android終端技術,又掌握web應用技術的程序員比較稀缺,隨著中國步入移動互聯網時代,企業對這種移動互聯網時代綜合性人才的需求很旺盛。如果不了解web應用技術,最終會遇到了技術和發展的瓶頸;另一方面,Google聯合OHA推出的真正優勢之一也在于和和互聯網結合,Google的用意之一也是想開辟新的終端去使用Google的優勢服務。
服務器端開發技術目前主流的有Sun的Java?EE、微軟的.NET,開源的以PHP和MySQL為代表的LAMP體系,我們該選擇哪一種呢?從理論上講,很多人傾向于選擇Java?EE,畢竟它們都是使用Java作為開發語言的,但是很多人面對Java?EE眾多的框架就望而生畏,其實在學習Java?EE的時候可以從Struts入手,隨著業務的需求逐步深入。當然,選擇微軟的.NET也行,畢竟該技術體系也占有很大?市場份額。其實,筆者認為,選擇LAMP可以是會獲得最高的“性價比”的,一方面PHP是現在Web方面的主流語言,大多數新型的網站尤其是創業性質的網站一般都會選用PHP作為服務端開發語言,另一方面,前面也說過,Android是為移動互聯而生的,兩者達到了完美的契合。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
