說起Android的性能優(yōu)化,其實是一個很大的范疇笛粘,說到深入的就是架構師級別的。但是對很對初中級的Android開發(fā)工程師們經(jīng)常遇到——
用戶say郑兴,什么狗屎,刷這么久都沒反應贝乎,取關卸載算了情连。又或者是老板拍板了,施壓給CTO览效,然后CTO又來找你:Y的今天必須給我想辦法優(yōu)化了却舀,不然不準回家。
參考40 Developer Tips for Android Optimization
那為什么從UI的表象上看朽肥,App又卡又慢而且還錯亂禁筏。基于這些衡招,leo就給大家整理出一些能輕易掌握一些應用開發(fā)中用到的性能優(yōu)化技巧篱昔,從而輕松應對這些情況。
一始腾、UI層
首先要明白的是UI的繪制流程:measure-layout-draw州刽,measure與layout都需要for loop所有的子控件,匯集起來才能完成繪制浪箭,布局穗椅。所以子控件越多,所消耗的時間越長(inflate奶栖,layout_weight匹表,relative,多層嵌套等)宣鄙,減少不必要的子控件或層級袍镀,是相當有必要的。你可以通過merge,viewstub這些標簽來減少層級嵌套冻晤。如果你的空間觀念沒那么好苇羡,可以用HierarchyViewer工具來檢查。
復用item鼻弧,會更輕松设江。對于Listview或者GridView這種多item的組件來說,復用item可以減少inflate次數(shù)攘轩,通過setTag叉存,getTag的ViewHolder方式實現(xiàn)復用,這里要注意的是度帮,holder中的控件最好reset后再賦值鹉胖,避免圖片,文字錯亂。
讓空白空間大于圖像空間甫菠,讓圖像空間大于按鈕的大小。如果將按鈕冕屯,多選框寂诱,切換控件放大后是很丑陋的。一個100dip(0.63")大小的按鈕是不想在平板上顯示為原來兩倍寬度200dip(1.25")的.原因是屏幕變大了安聘,這不是說平板是給巨人用的痰洒。我們可以這樣做,在按鈕增加的空間和圖片擴展的空間里添加空白浴韭。
pager最好不要預加載丘喻。對于ViewPager第一次顯示時卡頓以及左右滑動卡頓,有以下幾種優(yōu)化方式念颈。
同時緩存page數(shù)最好為最小值3泉粉,如果過多,那么第一次顯示時榴芳,ViewPager所初始化的pager就會很多嗡靡,這樣pager累積渲染耗時就會增多,看起來就卡窟感。
每個pager應該只在顯示時才加載網(wǎng)絡或數(shù)據(jù)庫(UserVisibleHint=true)讨彼,最好不要預加載數(shù)據(jù),以免造成浪費柿祈。
-
用GraphicalLayout工具快速預覽哈误。GraphicalLayout是WYSIWG XML編輯器。我喜歡直接編寫元素-而不是拖躏嚎,丟棄的可見編程方式蜜自,但在添加一些元素之后,可以在GraphicalLayout的下拉選擇菜單里選擇不同屏幕尺寸進行測試紧索。
Selectors是創(chuàng)建buttons的利器袁辈。我們在上面提到了如何在XML里定義button的背景,但是你將如何創(chuàng)建一個當按下去會改變的button呢珠漂?很簡單:像下面那樣在xml文件里定義背景晚缩。該xml文件將接收到button當前狀態(tài)并且在外觀上做出相應的改變。
在Honeycomb之前的版本里時不存在ActionBar跟很多animation樣式的媳危,所以可以使用ActionBarSherlock 跟NineOldAndroids來代替荞彼。Jake Wharton寫的Android開源 組件都是往下兼容的精心杰作。更為驚喜的是待笑,ABS 擁有強大的功能用來定義ActionBar鸣皂。
二、圖片
圖片顯示不出來或者加載時間太長,怎么辦寞缝?分兩部分癌压,加載速度,下載速度荆陆。
對于加載速度滩届,我們要知道一點,雖然下載的圖片可能只有幾百K被啼,但是decode成bitmap所占用的內(nèi)存可是成倍的帜消,盡可能的減小圖片size是根本因素,讓服務端提供不同分辨率的圖片才是最好的解決方案浓体,內(nèi)存總有耗盡的時刻泡挺,別老想著大分辨率會更清晰,實際就只有150&150的空間命浴,非給弄張1000&1000的圖片是不恰當?shù)穆γāA硗庹摷虞d速度:內(nèi)存>硬盤>網(wǎng)絡,合理的使用內(nèi)存緩存也是關鍵咳促。假如自己寫不好稚新,沒關系,有那么多開源的圖片緩存框架跪腹,不用自己操心褂删。
對于下載,要控制好同時下載的最大任務數(shù)(平均速度慢)冲茸,同時給InputStream再包一層緩沖流會更快(如BufferedInputStream)屯阀。
不要把所有的圖片都縮放了。用布局文件來適應不同屏幕尺寸的方法只是成功的一半轴术,布局里的元素(如:圖片)也要能在高分辨率的屏幕下良好工作难衰。在概念上比較簡單的方式就是創(chuàng)建一套完整的圖片目錄并將它們與很多drawable目錄匹配起來。
drawable-sw600dp-ldpi drawable-sw600dp-mdpi drawable-sw600dp-hdpi drawable-sw600dp-xhdpi drawable-sw600dp-xxhdpi ...其它的類似逗栽。 但是實際就不要太盡信書了盖袭。 一般來說有drawble-ldpi, drawable-hdpi等目錄就足夠了,不需要將所有的情況都加上彼宠。避免使用位圖(jpg,png)鳄虱。對于一些圖標來說,用位圖是個不錯的選擇凭峡,因為它們使用簡單拙已。但是如果可以避免使用位圖,你可以節(jié)省很多空間摧冀。但用不同的方法也可以達到很好的結果倍踪。
用XML繪圖系宫。位圖都可以用XML繪圖來代替的。XML繪圖不是萬能的建车,但是它的方便性還是使我感到驚訝扩借。你可以在布局文件的任何地方來引用,而且它可以適應于任何屏幕癞志。用它可以做出理想的按鈕往枷。如果必須,就用位圖凄杯。
通過覆蓋onDraw()創(chuàng)建自定義views. 有些事情XML并不十分在行,我們在OpenSignal和WeatherSignal中畫過許多圖像秉宿,為此有許多的庫戒突,但是我們要為自定義圖像自己編寫代碼。這很有趣描睦〔泊妫或許你永遠也不需要做這個,但為了使圖像高度動態(tài)并自定義忱叭,這經(jīng)常是唯一可行的辦法隔崎。
在不能使用XML的地方使用SVG. 有時候覆蓋onDraw()并勤勤懇懇的為自定義view編寫代碼畫出需要的線條與弧線是過于技術化了。畢竟有一種矢量圖像語言韵丑,它稱作…Scalable Vector Graphics(可擴展矢量圖形)爵卒。它也是史上最酷的Android應用之一—Androidify的動力來源。事實上他們創(chuàng)建這個庫就是為了那款應用撵彻,他們將它發(fā)布在這里:SVG for Android 钓株。
對SVG文件GZip壓縮。將它們變得更小它們就會處理的更快陌僵。 但是SVG庫并不是支持一切轴合。在一些特定的alpha通道中似乎不能正常工作,你甚至不得不在代碼中將它們剔除碗短。
如果要用PNG受葛,最好優(yōu)化一下(用PNGCrush或ImageOptim)
三、運行速度
在運行慢的手機上測試偎谁。你將在運行慢的手機上發(fā)現(xiàn)很多問題总滩,同時它讓你抓狂,沒人會喜歡運行慢的程序搭盾。盡量精簡你的代碼咳秉,你寫的層級與解析越多,就意味著系統(tǒng)需要花更多的時間鸯隅。
盡量減少XML布局層次澜建。更多的層次意味著系統(tǒng)將為解析你的代碼付出更多的工作向挖,這將會讓圖像渲染的更慢。用<merge>可以幫助你減少視圖層次結構炕舵。這是一種簡單的方式來去除多余的層次何之。好的文章都對此有所解釋,而且在 Android Developer中它也顯得與眾不同咽筋。
用Android Lint溶推。在工程目錄上右鍵選擇Eclipse>Android Tools>Run Lint。它將會得到程序的一些信息奸攻,并能提高程序的運行速度蒜危,或者它能讓你得代碼更加清爽。它可以給你的代碼提供很詳細的信息睹耐,并在你出錯之前就可以給做出提示辐赞。
用HierarchyViewer可以直觀的看到你布局的層次。這個智能的工具可以顯示布局中有多少層次硝训,而且可以提示出那些可以讓程序變慢响委。
-
如果可以盡量用RelativeLayout。AbsoluteLayout已經(jīng)過期了窖梁,就不要用了赘风。你經(jīng)常會遇到在RelativeLayout和LinearLayout中做出選擇的情況,那就直接用RelativeLayouot吧纵刘,因為它可以讓你減少視圖層次邀窃。
第二個表單比第一個難看的多。事實上彰导,我們已經(jīng)介紹過一個完整的新元素了蛔翅。但是假如我們要給每個盒子里加入一個圖片,一般的我們將這樣做:盒子 A 在屏幕左半邊 圖片|盒子 B在屏幕右半邊 圖片用第一中方法位谋,你得創(chuàng)建一個有兩個層次的LinearLayout山析,如果用第二種方法,你可以直接在同一個RelativeLayout中加入圖片掏父,比如要指定第一個圖片必須在“dummy_center”的左邊笋轨,而且一個TextView A必須也在其左側。那么你就得用7個元素3個視圖層次了(LinearLayout 方式)赊淑,而(RelativeLayout方式)只用6個元素2個層次爵政,這樣所有的工作添加完成。
用一些擴展工具如DDMS陶缺。這可以幫助你發(fā)現(xiàn)一些不必要的網(wǎng)絡調(diào)用钾挟、查看電池使用量、垃圾回收信息饱岸,狀態(tài)變化(例子:當回調(diào)onStop和onDestroy時)等掺出。LittleEye是我目前比較喜歡的工具徽千。
用AsyncTasks。Anroid工程團隊受夠了人們經(jīng)常在UI線程里面實現(xiàn)網(wǎng)絡調(diào)用(譯注:耗時操作汤锨,容易阻塞UI刷新)双抽,所以他們實現(xiàn)了一些可產(chǎn)生編譯級錯誤信息的API。但是仍然在很多app中的一些工作會拖垮UI線程闲礼,我們要考慮到UI布局要快以及提高UI的響應性牍汹。
四、緩存與網(wǎng)絡
有很多種緩存方式柬泽,也不用leo列舉了慎菲,我們要說的是搭配使用。優(yōu)化可不是一個人的事锨并,實現(xiàn)一個功能簡單钧嘶,但是想優(yōu)化重構,那是很不容易的事琳疏。需要多方面的預判與聯(lián)調(diào)。合理的假設與實踐是優(yōu)化最重要的手段闸拿。
軟引用空盼、弱引用。 比方說新荤,以前我們一直在用強引用揽趾,HashMap,后來我們發(fā)現(xiàn)占內(nèi)存苛骨,我們就用軟引用篱瞎,弱引用來及時回收,再后來因為回收機制不可控痒芝,所以又有了lrucache俐筋,disklrucache通過算法來平衡內(nèi)存與硬盤緩存。隨著android版本的推進與演化严衬,我們也應該擁抱變化澄者。如果你的App里還有軟引用,弱引用的地方请琳,不妨再check下粱挡。
網(wǎng)絡+數(shù)據(jù)庫。網(wǎng)絡我們一般都是去主動獲取俄精,而非被動接受询筏。那如果說數(shù)據(jù)是重復的或者未更改的呢?那我們?nèi)ト∫淮尉W(wǎng)絡數(shù)據(jù)有什么意義呢竖慧?我的解決方案是給每個activity或fragment或每個組件設置一個最大請求間隔嫌套,比如一個listview逆屡,第一次請求數(shù)據(jù)時,保存一份到數(shù)據(jù)庫灌危,并記下時間戳康二,當下次重新初始化時,判斷是否超過最大時間間隔(如5分鐘)勇蝙,如果沒有沫勿,只加載數(shù)據(jù)庫數(shù)據(jù),不需要再做網(wǎng)絡請求味混。當然产雹,還有一些隱式的http請求框架會緩存服務器數(shù)據(jù),在一定時間內(nèi)不再請求網(wǎng)絡翁锡,或者當服務器返回304時將之前緩存的數(shù)據(jù)直接返回蔓挖。
合并多個請求。現(xiàn)在有很多現(xiàn)成HTTP框架供我們使用馆衔,我們幾乎只用寫配置就可以搞定一個url請求瘟判,但是這里有很多需要服務端配合的,比如:json數(shù)據(jù)格式角溃,WebP代替jpg拷获,支持斷點續(xù)傳,多個請求合并成一個减细,盡量不做重定向匆瓜,服務器緩存以及負載均衡等。
控制請求并發(fā)量未蝌。對客戶端本身驮吱,除了上述的實現(xiàn),我們還需要合理的緩存萧吠,控制最大請求并發(fā)量左冬,及時取消已失效的請求,過濾重復請求怎憋,timeout時間設置又碌,請求優(yōu)先級設置等。
五绊袋、代碼規(guī)范
接著leo給大家 補充些代碼規(guī)范毕匀。
你要知道for loop中不要聲明臨時變量,不到萬不得已不要在里面寫try catch癌别。
明白垃圾回收機制皂岔,避免頻繁GC,內(nèi)存泄漏展姐,OOM躁垛。(有機會專門說)
合理使用數(shù)據(jù)類型剖毯,比如StringBuilder代替String,(筆試題最常見的是str+=”str”中有幾個對象) 教馆,少用枚舉enum逊谋,少用父類聲明。(List,Map)
如果你有頻繁的new線程土铺,那最好通過線程池去execute它們胶滋,減少線程創(chuàng)建開銷。
你要知道單例的好處悲敷,并正確的使用它究恤。
多用常量,少用顯式的”action_key”后德,并維護一個常量類部宿,別重復聲明這些常量。
如果可以瓢湃,至少要弄懂設計模式中的策略模式理张,組合模式,裝飾模式绵患,工廠模式涯穷,觀察者模式,這些能幫助你合理的解耦藏雏,即使需求頻繁變更,你也不用害怕牽一發(fā)而動全身作煌。需求變更不可怕掘殴,可怕的是沒有在寫代碼之前做合理的設計。
優(yōu)化當然還有很多很多粟誓,leo所說的也只是一個大的輪廓奏寨,還是需要自己不斷的嘗試。會開發(fā)寫代碼跟會做產(chǎn)品的區(qū)別還是蠻大的鹰服,僅僅是態(tài)度就能刷死80%的碼農(nóng)了病瞳。當你碰到一些需要優(yōu)化的地方,耐心的去分析悲酷,時間的累積會讓你成為真正的工程師套菜。
另外優(yōu)化也沒有絕對的完美,每一次優(yōu)化都是基于當前的業(yè)務來做的设易,要明白溝通是最好的優(yōu)化逗柴,不盲從,不隨便顿肺,三思而后行戏溺。整理出來的渣蜗,以上就這些了,希望對你們有幫助旷祸,有什么問題耕拷,歡迎聯(lián)系,一起提高技術知識托享!