針對Android的性能優(yōu)化杨拐,主要有以下幾個有效的優(yōu)化方法:
1.布局優(yōu)化
2.繪制優(yōu)化
3.內(nèi)存泄漏優(yōu)化
4.響應速度優(yōu)化
5.ListView/RecycleView及Bitmap優(yōu)化
6.線程優(yōu)化
7.其他性能優(yōu)化的建議
下面我們具體來介紹關于以上這幾個方面優(yōu)化的具體思路及解決方案。
二浪感、布局優(yōu)化
關于布局優(yōu)化的思想很簡單纤掸,就是盡量減少布局文件的層級蚯涮。這個道理很淺顯逢享,布局中的層級少了竞穷,就意味著Android繪制時的工作量少了,那么程序的性能自然就提高了。
如何進行布局優(yōu)化卑惜?
①刪除布局中無用的控件和層次膏执,其次有選擇地使用性能比較低的ViewGroup。
關于有選擇地使用性能比較低的ViewGroup,這就需要我們開發(fā)就實際靈活選擇了露久。
例如:如果布局中既可以使用LinearLayout也可以使用RelativeLayout更米,那么就采用LinearLayout,這是因為RelativeLayout的功能比較復雜毫痕,它的布局過程需要花費更多的CPU時間征峦。FrameLayout和LinearLayout一樣都是一種簡單高效的ViewGroup,因此可以考慮使用它們消请,但是很多時候單純通過一個LinearLayout或者FrameLayout無法實現(xiàn)產(chǎn)品效果栏笆,需要通過嵌套的方式來完成。這種情況下還是建議采用RelativeLayout,因為ViewGroup的嵌套就相當于增加了布局的層級臊泰,同樣會降低程序的性能蛉加。
②采用標簽: include,merge,ViewStub
標簽主要用于布局重用。
標簽一般要配合使用缸逃,可以降低減少布局的層級针饥。
ViewStub提供了按需加載的功能,當需要時才會將ViewStub中的布局加載到內(nèi)存需频,提高了程序初始化效率丁眼。
③避免多度繪制
過度繪制(Overdraw)描述的是屏幕上的某個像素在同一幀的時間內(nèi)被繪制了多次。在多層次重疊的 UI 結構里面昭殉,如果不可見的 UI 也在做繪制的操作苞七,會導致某些像素區(qū)域被繪制了多次,同時也會浪費大量的 CPU 以及 GPU 資源饲化。
如下所示莽鸭,有些部分在布局時,會被重復繪制吃靠。
關于過度繪制產(chǎn)生的一般場景及解決方案硫眨,參考:Android 過度繪制優(yōu)化
三、繪制優(yōu)化
繪制優(yōu)化是指View的onDraw方法要避免執(zhí)行大量的操作巢块,這主要體現(xiàn)在兩個方面:
①onDraw中不要創(chuàng)建新的局部對象礁阁。
因為onDraw方法可能會被頻繁調(diào)用,這樣就會在一瞬間產(chǎn)生大量的臨時對象族奢,這不僅占用了過多的內(nèi)存而且還會導致系統(tǒng)更加頻繁gc姥闭,降低了程序的執(zhí)行效率。
②onDraw方法中不要做耗時的任務越走,也不能執(zhí)行成千上萬次的循環(huán)操作棚品,盡管每次循環(huán)都很輕量級,但是大量的循環(huán)仍然十分搶占CPU的時間片门怪,這會造成View的繪制過程不流暢锅纺。
按照Google官方給出的性能優(yōu)化典范中的標準掷空,View的繪制頻率保證60fps是最佳的,這就要求每幀繪制時間不超過16ms(16ms = 1000/60)囤锉,雖然程序很難保證16ms這個時間,但是盡量降低onDraw方法中的復雜度總是切實有效的酿傍。
四、內(nèi)存泄漏優(yōu)化
內(nèi)存泄漏是開發(fā)過程中的一個需要重視的問題区丑,但是由于內(nèi)存泄露問題對開發(fā)人員的經(jīng)驗和開發(fā)意識有較高的要求拧粪,因此也是開發(fā)人員最容易犯的錯誤之一修陡。
內(nèi)存泄露的優(yōu)化分為兩個方面:
①在開發(fā)過程中避免寫出有內(nèi)存泄漏的代碼
②通過一些分析工具比如MAT來找出潛在的內(nèi)存泄露魄鸦,然后解決宴杀。
對應于兩種不同情況拾因,一個是了解內(nèi)存泄漏的可能場景以及如何規(guī)避绢记,二是怎么查找內(nèi)存泄漏扁达。
1.那么我們就先了解什么是內(nèi)存泄漏?這樣我們才能知道如何避免蠢熄。
大家都知道,java是有垃圾回收機制的叉讥,這使得java程序員比C++程序員輕松了許多饥追,存儲申請了,不用心心念念要加一句釋放但绕,java虛擬機會派出一些回收線程兢兢業(yè)業(yè)不定時地回收那些不再被需要的內(nèi)存空間(注意回收的不是對象本身,而是對象占據(jù)的內(nèi)存空間)六孵。
Q1:什么叫不再被需要的內(nèi)存空間?
答:Java沒有指針莹桅,全憑引用來和對象進行關聯(lián)烛亦,通過引用來操作對象诈泼。如果一個對象沒有與任何引用關聯(lián),那么這個對象也就不太可能被使用到了煤禽,回收器便是把這些“無任何引用的對象”作為目標铐达,回收了它們占據(jù)的內(nèi)存空間。
Q2:如何分辨為對象無引用瓮孙?
答:2種方法
引用計數(shù)法直接計數(shù)选脊,簡單高效,Python便是采用該方法偏灿。但是如果出現(xiàn) 兩個對象相互引用钝的,即使它們都無法被外界訪問到,計數(shù)器不為0它們也始終不會被回收沿猜。為了解決該問題碗脊,java采用的是b方法。
可達性分析法這個方法設置了一系列的“GC Roots”對象作為索引起點疟游,如果一個對象 與起點對象之間均無可達路徑痕支,那么這個不可達的對象就會成為回收對象。這種方法處理 兩個對象相互引用的問題卧须,如果兩個對象均沒有外部引用,會被判斷為不可達對象進而被回收(如下圖)笋籽。
Q3:有了回收機制,放心大膽用不會有內(nèi)存泄漏笛园?
答:答案當然是No侍芝!
雖然垃圾回收器會幫我們干掉大部分無用的內(nèi)存空間,但是對于還保持著引用棵红,但邏輯上已經(jīng)不會再用到的對象咧栗,垃圾回收器不會回收它們。這些對象積累在內(nèi)存中交煞,直到程序結束可岂,就是我們所說的“內(nèi)存泄漏”。 當然了,用戶對單次的內(nèi)存泄漏并沒有什么感知纸淮,但當泄漏積累到內(nèi)存都被消耗完咽块,就會導致卡頓,崩潰侈沪。
下面這張圖可以幫助我們更好地理解對象的狀態(tài)亭罪,以及內(nèi)存泄漏的情況
左邊未引用的對象是會被GC回收的,右邊被引用的對象不會被GC回收情组,但是未使用的對象中除了未引用的對象,還包括已被引用的一部分對象闲询,那么內(nèi)存泄漏久發(fā)生這部分已被引用但未使用的對象底瓣。
2.Android一般在什么情況下會出現(xiàn)內(nèi)存泄漏?
①集合類泄漏 ②單例/靜態(tài)變量造成的內(nèi)存泄漏 ③匿名內(nèi)部類/非靜態(tài)內(nèi)部類 ④資源未關閉造成的內(nèi)存泄漏
大概可以分為以上幾類键耕,還有一些經(jīng)常會聽到的Hanlder,Thread,AsyncTask引起內(nèi)存泄漏柑营,都屬于上述③中的情況。
那么上述四種情況是怎么造成的內(nèi)存泄漏酒奶,具體是什么原因奶赔,以及Android中一些知名的引起內(nèi)存泄漏的原因,以及解決方法是怎么樣的另伍?
3.Android怎么分析內(nèi)存泄漏绞旅?
上面介紹了內(nèi)存泄漏的場景,對應的有一些解決方案因悲。
那么在內(nèi)存泄漏已經(jīng)發(fā)生的情況下晃琳,我們該如何解決呢?
我們可以通過MAT(Memory Analyzer Tool)人灼,或者 LeakCanary來檢測Android中的內(nèi)存泄漏顾翼。
五、響應速度優(yōu)化
響應速度優(yōu)化的核心思想就是避免在主線程中做耗時操作跪呈。
如果有耗時操作,可以開啟子線程執(zhí)行耗绿,即采用異步的方式來執(zhí)行耗時操作误阻。
如果在主線程中做太多事情,會導致Activity啟動時出現(xiàn)黑屏現(xiàn)象究反,甚至ANR精耐。
Android規(guī)定,Activity如果5秒鐘之內(nèi)無法響應屏幕觸摸事件或者鍵盤輸入事件就會出現(xiàn)ANR向胡,而BroadcastReceiver如果10秒鐘之內(nèi)還未執(zhí)行完操作也會出現(xiàn)ANR惊完。
為了避免ANR,可以開啟子線程執(zhí)行耗時操作拇派,但是子線程不能更新UI凿跳,所以需要子線程與主線程進行通信來解決子線程執(zhí)行耗時任務后,通知主線程更新UI的場景苟径。關于這部分躬审,需要掌握Handler消息機制蟆盐,AsyncTask,IntentService等內(nèi)容博助。
然而痹愚,在實際開發(fā)中蛔糯,ANR仍然不可避免的發(fā)生了蚁飒,而且很難從代碼上發(fā)現(xiàn)萝喘,這時候就要用到ANR日志分析。當一個進程發(fā)生了ANR之后爬早,系統(tǒng)會在/data/anr目錄下創(chuàng)建一個文件traces.txt启妹,通過分析這個文件就能定位出ANR的原因。
六桨啃、ListView/RecycleView及Bitmap優(yōu)化
ListView/RecycleView的優(yōu)化思想主要從以下幾個方面入手:
①使用ViewHolder模式來提高效率
②異步加載:耗時的操作放在異步線程中
③ListView/RecycleView的滑動時停止加載和分頁加載
具體優(yōu)化建議及詳情咙崎,參考:ListView的優(yōu)化
Bitmap優(yōu)化
主要是對加載圖片進行壓縮褪猛,避免加載圖片多大導致OOM出現(xiàn)。
七碳却、線程優(yōu)化
線程優(yōu)化的思想就是****采用線程池笑旺,避免程序中存在大量的Thread。線程池可以重用內(nèi)部的線程关噪,從而避免了線程的創(chuàng)建和銷毀鎖帶來的性能開銷乌妙,同時線程池還能有效地控制線程池的最大并法術,避免大量的線程因互相搶占系統(tǒng)資源從而導致阻塞現(xiàn)象的發(fā)生虐沥。因此在實際開發(fā)中,盡量采用線程池镐依,而不是每次都要創(chuàng)建一個Thread對象天试。
八秋秤、其他性能優(yōu)化建議
①避免過度的創(chuàng)建對象
②不要過度使用枚舉,枚舉占用的內(nèi)存空間要比整型大
③常量請使用static final來修飾
④使用一些Android特有的數(shù)據(jù)結構灼卢,比如SparseArray和Pair等
⑤適當采用軟引用和弱引用
⑥采用內(nèi)存緩存和磁盤緩存
⑦盡量采用靜態(tài)內(nèi)部類鞋真,這樣可以避免潛在的由于內(nèi)部類而導致的內(nèi)存泄漏。
以上是關于Android性能優(yōu)化方面海诲,我們一些入手點檩互。從這些方面,我們可以在平時的開發(fā)中注意蚯斯,避免類似錯誤饵较,提高Android程序的性能,但是其中一些方面的要求則需要我們不斷的學習循诉,以及平時良好的意識與習慣茄猫。由于自己開發(fā)經(jīng)驗幾乎為0,沒辦法根據(jù)實際經(jīng)驗來說明炫惩,只能寫下這篇文章來提醒自己以后開發(fā)的時候需要注意和培養(yǎng)的地方阿浓。