Android 性能優(yōu)化

1.什么是性能優(yōu)化

百度百科:

性能優(yōu)化(Optimize)
簡(jiǎn)而言之昧狮,就是在不影響系統(tǒng)運(yùn)行正確性的前提下,使之運(yùn)行地更快鞋真,完成特定功能所需的時(shí)間更短。

維基百科:

大多數(shù)系統(tǒng)會(huì)響應(yīng)增加的負(fù)載而導(dǎo)致性能會(huì)有一定程度的下降亿虽,修改系統(tǒng)以處理更高負(fù)載就是性能優(yōu)化。

總結(jié)就是苞也,提高負(fù)載能力讓程序運(yùn)行更快洛勉,用更少的資源做更多的活就是性能優(yōu)化。
2.為什么要性能優(yōu)化
隨著科技不斷發(fā)展如迟、移動(dòng)互聯(lián)網(wǎng)的迅猛發(fā)展收毫,手機(jī)硬件不斷進(jìn)步以及使用手機(jī)的人口增多,這樣就導(dǎo)致我們的程序的實(shí)際運(yùn)行環(huán)境是無(wú)法控制的殷勘,除開(kāi)程序本身的質(zhì)量而言此再,我們不能完全拋棄低端手機(jī)用戶群體,這是我們的人口紅利玲销,一句話簡(jiǎn)而概之输拇,我們要提升用戶留存,不能讓程序在低端手機(jī)運(yùn)行不流暢甚至ANR贤斜。

2015 年上半年策吠,Pinterest 的工程師進(jìn)行了一次實(shí)驗(yàn),借此將移動(dòng) Web 首頁(yè)的頁(yè)面加載性能提升了 60%瘩绒,同時(shí)移動(dòng)注冊(cè)轉(zhuǎn)化率提升了 40%猴抹。然而該實(shí)驗(yàn)使用了一種極為煩瑣的解決方案,用到了大量“抄近道”的方法锁荔,例如提供預(yù)先生成的 HTML 頁(yè)面蟀给,而沒(méi)有使用內(nèi)部模版渲染引擎或其他通用資源(JS、CSS)阳堕。為了將實(shí)驗(yàn)學(xué)到的經(jīng)驗(yàn)實(shí)用化跋理,整個(gè)前端引擎、所有頁(yè)面模版嘱丢,以及通用元素都必須重寫薪介。

亞馬遜近10年前的一項(xiàng)研究證明,即使在那時(shí)越驻,頁(yè)面加載時(shí)間每減少100毫秒,收入也會(huì)增加1%道偷。最近的另一項(xiàng)研究強(qiáng)調(diào)了這樣一個(gè)事實(shí):超過(guò)一半的受訪網(wǎng)站所有者表示缀旁,由于應(yīng)用程序性能不佳,他們失去了收入或客戶勺鸦。

總結(jié)并巍,產(chǎn)品的意義是解決現(xiàn)實(shí)生活的需求,一個(gè)好的產(chǎn)品必定有著優(yōu)秀的性能换途,而優(yōu)秀甚至極致的性能能夠提升用戶的主觀感受懊渡,讓用戶愿意繼續(xù)消費(fèi)刽射,也為后續(xù)的用戶轉(zhuǎn)化打下基礎(chǔ)。

3.如何性能優(yōu)化
至于如何具體的深入到項(xiàng)目中去進(jìn)行性能優(yōu)化呢

1剃执、快速響應(yīng)用戶的觸碰事件(不要在主線程干耗時(shí)操作)
2誓禁、設(shè)置動(dòng)畫或滾動(dòng)時(shí),在16毫秒以內(nèi)生成幀
3肾档、最大程度的減少內(nèi)存分配摹恰,避免短時(shí)大量分配內(nèi)存(頻繁的GC會(huì)造成內(nèi)存抖動(dòng),
JVM在進(jìn)行回收時(shí)會(huì)發(fā)出stop world 指令怒见,該指令會(huì)暫停所有線程俗慈,從而導(dǎo)致UI卡頓)
4、持續(xù)吸引用戶遣耍,一個(gè)頁(yè)面的數(shù)據(jù)盡可能在1000毫秒以內(nèi)呈現(xiàn)交互內(nèi)容
渲染闺阱、計(jì)算、內(nèi)存舵变、網(wǎng)絡(luò)酣溃、電量

Render
首先說(shuō)說(shuō)渲染方面,在正式開(kāi)車之前棋傍,我們先了解一下其他的知識(shí)點(diǎn)救拉,Android系統(tǒng)每隔16ms就重新繪制一次頁(yè)面,就是說(shuō)應(yīng)用要在16ms內(nèi)完成屏幕刷新瘫拣,如果16ms內(nèi)沒(méi)有渲染完畢亿絮,那就是我們常說(shuō)的卡頓、不跟手麸拄,專業(yè)的說(shuō)法就是掉幀派昧。至于安卓系統(tǒng)為什么設(shè)置每個(gè)16ms來(lái)觸發(fā)渲染這就跟人眼的生理結(jié)構(gòu)有關(guān),人的眼睛可以感知每秒60幀的動(dòng)畫拢切,如果低于60幀就不會(huì)認(rèn)為它是連續(xù)性的蒂萎。

頁(yè)面渲染的步驟

舉個(gè)例子,比如一個(gè)按鈕 Button 淮椰,LayoutInflater遍歷 XML 文件然后把 Button 實(shí)例加載到內(nèi)存五慈,內(nèi)存里面保存了 width、height主穗、left泻拦、top、right忽媒、bottom以及一些內(nèi)外邊距争拐,CPU經(jīng)過(guò)計(jì)算生成多維向量圖形,最后將計(jì)算好的圖形給GPU來(lái)進(jìn)行柵格化像素填充晦雨。
柵格化

柵格化

所有在屏幕上我們看到的圖片放大一百倍架曹,可以發(fā)現(xiàn)就是一個(gè)個(gè)發(fā)光點(diǎn)隘冲,屏幕的顏色呈像原理是基于三基色RGB,所有的顏色的都是由RGB組成绑雄,或者再加上Alpha透明度展辞,這也不難發(fā)覺(jué)為什么我們做圖片優(yōu)化的時(shí)將 ARGB8888改成ARGB4444、RGB565绳慎,一個(gè)8進(jìn)制位是一個(gè)字節(jié)纵竖,一個(gè)ARGB8888像素點(diǎn)的呈像信息是4個(gè)字節(jié),ARGB4444杏愤、RGB565將內(nèi)存降低了一半靡砌,縮小寬高和矩陣壓縮也是同理,通過(guò)控制寬高來(lái)降低內(nèi)存開(kāi)銷珊楼。
具體在實(shí)際的安卓開(kāi)發(fā)中通殃,我們要盡量避免過(guò)度繪制、XML層級(jí)過(guò)深厕宗、測(cè)量耗時(shí)等
過(guò)度繪制

我們知道安卓是根據(jù)XML從上而下遍歷渲染的画舌,圖中的藍(lán)色區(qū)域是一次繪制,綠色區(qū)域的控件也有顏色已慢,這樣就造成了二次繪制曲聂,以此類推,這就是過(guò)度繪制佑惠。根據(jù)渲染的原理朋腋,CPU通過(guò)計(jì)算后交給GPU來(lái)柵格化,過(guò)度繪制就導(dǎo)致我們做了很多無(wú)用的計(jì)算膜楷。
關(guān)于XML層級(jí)過(guò)深的問(wèn)題旭咽,這個(gè)無(wú)需多說(shuō),頁(yè)面通過(guò)setContentView加載到頁(yè)面是通過(guò)XML遍歷處理的赌厅,層級(jí)越深速度越慢穷绵,一般情況下能夠用 LinearLayout 就不要用 RelativeLayout ,RelativeLayout 的測(cè)量會(huì)觸發(fā)兩次特愿,測(cè)量左右關(guān)系仲墨、然后上下關(guān)系,LinearLayout 在沒(méi)有用 weight 時(shí)只測(cè)量一次就能夠確定位置效率相對(duì)比較高揍障,比如一個(gè)布局需要最左邊和最右邊都顯示一個(gè)按鈕宗收,這種布局用 FrameLayout 最合適,因?yàn)镕rameLayout是效率最高的 ViewGroup亚兄。
線性布局

幀布局

以及一些組合類型的自定義View,當(dāng)我們的自定義View繼承了響應(yīng)的 ViewGroup 時(shí)采驻,然后XML里面又有一個(gè)父布局审胚,這樣多了一層嵌套匈勋,這個(gè)時(shí)候可以通過(guò) merge 標(biāo)簽來(lái)去除。

Compute
計(jì)算方面的優(yōu)化比較雜膳叨,舉個(gè)例子

ArrayList list = new ArrayList();
 for(int i = 0; i < list.size(); i++){
            
}

上面這個(gè)for 循環(huán)是低效的洽洁,尤其是大數(shù)據(jù)量的循環(huán)尤為明顯,建議改為

ArrayList list = new ArrayList();
 for(int i = 0, y =  list.size(); i <y; i++){
            
}

在安卓系統(tǒng)中菲嘴,谷歌為我們提供了一些平臺(tái)比較高效的數(shù)據(jù)結(jié)構(gòu)饿自,android.util包下一共有如下幾個(gè)類:SparseArray系列(SparseArray,SparseBooleanArray龄坪,SparseIntArray昭雌,SparseLongArray,LongSparseArray)健田, SparseArray 在安卓平臺(tái)上效率高于 HashMap烛卧,這些特定的數(shù)據(jù)結(jié)構(gòu)從讀取速度、內(nèi)存消耗都有做特殊的優(yōu)化妓局,可以在合適的地方采用來(lái)提高效率总放。當(dāng)然Java傳統(tǒng)的數(shù)據(jù)結(jié)構(gòu)也并不是一無(wú)是處,合適的數(shù)據(jù)運(yùn)用在合適的業(yè)務(wù)場(chǎng)景看個(gè)人抉擇好爬,比如讀取場(chǎng)景比較頻繁的建議采用 ArrayList 線性隊(duì)列局雄,添加移除比較頻繁的則選擇 LinkedList ,建議使用 StringBuilder 來(lái)代替 + 拼接字符串存炮,字符串轉(zhuǎn)換建議使用String.valueOf() 炬搭,強(qiáng)轉(zhuǎn)和字符串拼接轉(zhuǎn) String 比較低效率,因?yàn)?String.valueOf() JVM 對(duì)這個(gè)方法做了特定的性能優(yōu)化僵蛛,一些大數(shù)據(jù)量的計(jì)算建議放在子線程執(zhí)行尚蝌,最好用線程池來(lái)操作,直接使用線程不可控充尉,可使用RxJava 的 IO 線程來(lái)處理大計(jì)算量飘言,很多框架有做相關(guān)的優(yōu)化,還有一些"計(jì)算優(yōu)化"是屬于特定的安卓下的計(jì)算優(yōu)化驼侠,比如我們 RecycleView 上滑刷新數(shù)據(jù)時(shí)最好不要用 notifyDataSetChanged(); 建議使用 notifyItemInserted();還有item移除用 notifyItemRemoved();有很多 api 是可以高效率處理的姿鸿,比如Gif動(dòng)態(tài)圖的加載,Glide雖然也可以加載倒源,但是計(jì)算量和內(nèi)存開(kāi)銷是比較大的苛预,這個(gè)時(shí)候通過(guò) JNI 通訊采用 giflib 來(lái)加載 Gif 動(dòng)態(tài)圖就比較高效了,giflib 是一個(gè) native 的框架笋熬,這樣做的好處就是 C 的運(yùn)行速度比 Java 快热某,而且 Native 分配的內(nèi)存不受限制,不會(huì) OOM,建議使用直接類型 int > Integer昔馋,計(jì)算優(yōu)化的方式千千萬(wàn)筹吐,看每個(gè)人實(shí)際的業(yè)務(wù)場(chǎng)景。


優(yōu)化秘遏,處于萬(wàn)物之間

Memory
內(nèi)存優(yōu)化丘薛,簡(jiǎn)單說(shuō)圖片優(yōu)化不外乎寬高、質(zhì)量邦危、矩陣縮放洋侨,然后大圖預(yù)覽局部渲染,在實(shí)際的開(kāi)發(fā)過(guò)程中有很多優(yōu)秀的第三方框架已經(jīng)幫我們做了優(yōu)化倦蚪,比如Glide的圖片加載策略希坚,先從內(nèi)存中尋找,沒(méi)有則去磁盤找审丘,再?zèng)]有則請(qǐng)求網(wǎng)絡(luò)圖片吏够,當(dāng)下載完畢保存到內(nèi)存和磁盤,這里就要提到一個(gè)算法 LruCache滩报,最近最少用到锅知,簡(jiǎn)單的說(shuō)就是,當(dāng)內(nèi)存不足時(shí)脓钾,最少被用的圖片會(huì)被回收售睹,像我們?cè)陂_(kāi)發(fā)中,如果不是非常有必要可训,建議不要使用反射昌妹,因?yàn)榉瓷鋾?huì)生成大量的臨時(shí)變量,生成變量開(kāi)辟內(nèi)存空間耗時(shí)握截,同時(shí)GC回收也耗時(shí)飞崖,有一些在程序運(yùn)行之前就可以確定的數(shù)據(jù)建議直接根據(jù)數(shù)量初始化,避免浪費(fèi)內(nèi)存谨胞,比如首頁(yè)有4個(gè)Fragment固歪,那我要用一個(gè)隊(duì)列來(lái)保存它,那就直接 ArrayList<Fragment> fragments = new ArrayList<>(4);

Network
網(wǎng)絡(luò)優(yōu)化


關(guān)于服務(wù)器端的網(wǎng)絡(luò)優(yōu)化不做過(guò)多解釋胯努,關(guān)于客戶端的連接優(yōu)化牢裳,IP直接鏈接,這個(gè)涉及到Http的原理叶沛,我們請(qǐng)求一個(gè)接口蒲讯,實(shí)際上請(qǐng)求一臺(tái)電腦的數(shù)據(jù),每一個(gè)生產(chǎn)環(huán)境的地址都是需要通過(guò)NDS服務(wù)器來(lái)解析的灰署,如果直接訪問(wèn)IP是可以優(yōu)化連接速度判帮,提高網(wǎng)絡(luò)性能優(yōu)化局嘁,很重要的一點(diǎn)就是降低延遲和提升響應(yīng)速度。
通常我們?cè)跒g覽器中發(fā)起請(qǐng)求的時(shí)候header部分往往是這樣的

keep-alive 就是瀏覽器和服務(wù)端之間保持長(zhǎng)連接脊另,這個(gè)連接是可以復(fù)用的导狡。在HTTP1.1中是默認(rèn)開(kāi)啟的。

連接的復(fù)用為什么會(huì)提高性能呢偎痛?
通常我們?cè)诎l(fā)起http請(qǐng)求的時(shí)候首先要完成tcp的三次握手,然后傳輸數(shù)據(jù)独郎,最后再釋放連接踩麦。三次握手的過(guò)程可以參考這里 TCP三次握手詳解及釋放連接過(guò)程

一次響應(yīng)的過(guò)程

在高并發(fā)的請(qǐng)求連接情況下或者同個(gè)客戶端多次頻繁的請(qǐng)求操作,無(wú)限制的創(chuàng)建會(huì)導(dǎo)致性能低下氓癌。

如果使用keep-alive


在timeout空閑時(shí)間內(nèi)谓谦,連接不會(huì)關(guān)閉,相同重復(fù)的request將復(fù)用原先的connection贪婉,減少握手的次數(shù)反粥,大幅提高效率。并非keep-alive的timeout設(shè)置時(shí)間越長(zhǎng)疲迂,就越能提升性能才顿,長(zhǎng)久不關(guān)閉會(huì)造成過(guò)多的僵尸連接和泄露連接出現(xiàn)。合理的復(fù)用時(shí)間能夠提升效率尤蒿,okttp也做了類似于keep-alive的鏈接復(fù)用機(jī)制郑气。
合并請(qǐng)求就是能夠一個(gè)接口返回的不要通過(guò)兩三個(gè)接口去請(qǐng)求,同一個(gè)頁(yè)面的數(shù)據(jù)初始化最好一個(gè)接口帶回腰池。并發(fā)連接很好理解尾组,高效率利用CPU,避免CPU閑置示弓。分優(yōu)先級(jí)請(qǐng)求網(wǎng)絡(luò)就是比如一個(gè)頁(yè)面分為頭部和尾部讳侨,首先展示給用戶的是頭部區(qū)域,那我們就先請(qǐng)求頭部數(shù)據(jù)奏属,等頭部數(shù)據(jù)出來(lái)后再請(qǐng)求尾部數(shù)據(jù)跨跨,這屬于策略請(qǐng)求優(yōu)化。關(guān)于數(shù)據(jù)優(yōu)化就是字面意思拍皮,不做解釋歹叮。

Battery
電量?jī)?yōu)化,其實(shí)主要就是注意自己的一些代碼問(wèn)題铆帽,有些操作沒(méi)有及時(shí)或正確的關(guān)閉會(huì)耗費(fèi)大量的電量咆耿,我們可以在得到充電狀態(tài)信息之后,有針對(duì)性的對(duì)部分代碼做優(yōu)化爹橱。比如我們可以判斷只有當(dāng)前 手機(jī)為 AC 充電狀態(tài)時(shí)才去執(zhí)行一些非常耗電的操作萨螺,像定位、傳感器用完記得及時(shí)關(guān)閉,使用傳感器慰技,選擇合適的采樣率椭盏,越高的采樣率類型則越費(fèi)電。后臺(tái)下載耗時(shí)任務(wù)建議使用JobScheduler吻商,其工作方式有 利于用戶在適當(dāng)?shù)臅r(shí)機(jī)執(zhí)行正確的事情掏颊。應(yīng)用可以在安排作業(yè)的同時(shí)允許系統(tǒng)基于內(nèi)存、電源 和連接情況進(jìn)行優(yōu)化艾帐。JobSchedule 的宗旨就是把一些不是特別緊急的任務(wù)放到更合適的時(shí)機(jī) 批量處理乌叶。這樣做有兩個(gè)好處:避免頻繁的喚醒硬件模塊,造成不必要的電量消耗柒爸。避免在不合適的時(shí)間(例如低電量情況下准浴、弱網(wǎng)絡(luò)或者移動(dòng)網(wǎng)絡(luò)情況下的)執(zhí)行過(guò)多的任務(wù)消耗電量。


關(guān)于后續(xù)
1捎稚、并發(fā)的優(yōu)化
2乐横、單例模式的選擇優(yōu)化
3、自定義View的注意事項(xiàng)
4今野、動(dòng)態(tài)代理葡公、反射的優(yōu)化
5、ART虛擬機(jī)和Dalvik的區(qū)別
6腥泥、View 的繪制流程
7匾南、APK包的瘦身優(yōu)化
8、應(yīng)用的啟動(dòng)優(yōu)化
9蛔外、dex文件的優(yōu)化
......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛆楞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夹厌,更是在濱河造成了極大的恐慌豹爹,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矛纹,死亡現(xiàn)場(chǎng)離奇詭異臂聋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)或南,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門孩等,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人采够,你說(shuō)我怎么就攤上這事肄方。” “怎么了蹬癌?”我有些...
    開(kāi)封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵权她,是天一觀的道長(zhǎng)虹茶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)隅要,這世上最難降的妖魔是什么蝴罪? 我笑而不...
    開(kāi)封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮步清,結(jié)果婚禮上要门,老公的妹妹穿的比我還像新娘。我一直安慰自己尼啡,他們只是感情好暂衡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著崖瞭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撑毛。 梳的紋絲不亂的頭發(fā)上书聚,一...
    開(kāi)封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音藻雌,去河邊找鬼雌续。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胯杭,可吹牛的內(nèi)容都是我干的驯杜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼做个,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鸽心!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起居暖,我...
    開(kāi)封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤顽频,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后太闺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糯景,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年省骂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蟀淮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钞澳,死狀恐怖怠惶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情略贮,我是刑警寧澤甚疟,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布仗岖,位于F島的核電站,受9級(jí)特大地震影響览妖,放射性物質(zhì)發(fā)生泄漏轧拄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一讽膏、第九天 我趴在偏房一處隱蔽的房頂上張望檩电。 院中可真熱鬧,春花似錦府树、人聲如沸俐末。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卓箫。三九已至,卻和暖如春垄潮,著一層夾襖步出監(jiān)牢的瞬間烹卒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工弯洗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旅急,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓牡整,卻偏偏與公主長(zhǎng)得像藐吮,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逃贝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354