Android開(kāi)發(fā)十年,這是我的性能優(yōu)化方案總結(jié)
現(xiàn)在安卓系統(tǒng)無(wú)論是性能還是體驗(yàn)上其實(shí)都不輸于iOS,只是因?yàn)槭謾C(jī)廠商多而雜,他們會(huì)改源碼,自定義系統(tǒng),最后又過(guò)一遍不同開(kāi)發(fā)水平工程師的手,導(dǎo)致很多手機(jī)即使在機(jī)器上面的跑分非常高,里面的APP運(yùn)行也有卡頓現(xiàn)象。
而且這種卡頓會(huì)隨著產(chǎn)品的更新迭代,功能的越發(fā)復(fù)雜,UI頁(yè)面的越發(fā)豐富,變得更加嚴(yán)重。
但是,產(chǎn)品功能的更新需求,新功能的開(kāi)發(fā)和UI的豐富都是用戶的需求,是不可逆的趨勢(shì)。在這樣的情況下,優(yōu)秀的性能優(yōu)化人才一直是幾大頭部互聯(lián)網(wǎng)公司高價(jià)競(jìng)聘的對(duì)象。
今天在這里根據(jù)我自己多年的開(kāi)發(fā)經(jīng)驗(yàn)來(lái)做一下安卓性能優(yōu)化方案的總結(jié)。
流暢(解決:卡頓)
穩(wěn)定(解決:內(nèi)存溢出、崩潰)
低耗損(解決:耗電快、流量大、網(wǎng)絡(luò)慢)
小安裝包(解決:APK過(guò)大)
布局優(yōu)化
繪制優(yōu)化
網(wǎng)絡(luò)優(yōu)化
APK優(yōu)化
內(nèi)存優(yōu)化
卡頓優(yōu)化
耗電優(yōu)化
ListView/RecycleView及Bitmap/圖片優(yōu)化
數(shù)據(jù)庫(kù)SQLite優(yōu)化
啟動(dòng)優(yōu)化
數(shù)據(jù)結(jié)構(gòu)優(yōu)化
穩(wěn)定性優(yōu)化
布局優(yōu)化
本質(zhì):減少View的層級(jí),提高測(cè)量、布局和繪制的速度。
常用方案:
優(yōu)先選擇LinearLayout布局可以減少View的層級(jí)(注意相同組件可能RelativeLayout繪制時(shí)間長(zhǎng));
使用 < include > 標(biāo)簽抽取常用的布局組件中的共同部分(便于復(fù)用);
用 < ViewStub > 標(biāo)簽加載不常用的布局,延遲加載(需要的時(shí)候在activity中加載出來(lái));
用 < Merge > 標(biāo)簽減少布局的嵌套層次
繪制優(yōu)化
本質(zhì):View的onDraw方法要避免執(zhí)行大量的操作
常用方案:
onDraw中不要?jiǎng)?chuàng)建新的局部對(duì)象(避免產(chǎn)生大量的臨時(shí)對(duì)象占用過(guò)多內(nèi)存);
onDraw方法中不要做耗時(shí)的任務(wù)(盡量降低onDraw方法中的復(fù)雜度)
網(wǎng)絡(luò)優(yōu)化
本質(zhì):減少流量消耗、電量消耗、用戶等待時(shí)間,提高用戶體驗(yàn)。
常用方案:
盡量減少網(wǎng)絡(luò)請(qǐng)求,能夠合并的就盡量合并
避免DNS解析,根據(jù)域名查詢可能會(huì)耗費(fèi)上百毫秒的時(shí)間,也可能存在DNS劫持的風(fēng)險(xiǎn)。可以根據(jù)業(yè)務(wù)需求采用增加動(dòng)態(tài)更新IP的方式,或者在IP方式訪問(wèn)失敗時(shí)切換到域名訪問(wèn)方式。
大量數(shù)據(jù)的加載采用分頁(yè)的方式
網(wǎng)絡(luò)數(shù)據(jù)傳輸采用GZIP壓縮
加入網(wǎng)絡(luò)數(shù)據(jù)的緩存,避免頻繁請(qǐng)求網(wǎng)絡(luò)
上傳圖片時(shí),在必要的時(shí)候壓縮圖片
APK優(yōu)化
本質(zhì):減少安裝包體積。
常用方案:
減少應(yīng)用中不必要的資源文件,比如圖片,在不影響APP效果的情況下盡量壓縮圖片,有一定的效果
在使用了SO庫(kù)的時(shí)候優(yōu)先保留v7版本的SO庫(kù),刪掉其他版本的SO庫(kù)。
res資源優(yōu)化
代碼優(yōu)化
lib資源優(yōu)化
assets資源優(yōu)化
代碼混淆
插件化
7z極限壓縮
PS:詳細(xì)具體的操作實(shí)現(xiàn)實(shí)現(xiàn)原理,后文另外有專(zhuān)門(mén)的分析。
內(nèi)存優(yōu)化
本質(zhì):避免內(nèi)存泄漏、擴(kuò)大內(nèi)存。
常用方案(從不同方向討論):
擴(kuò)大內(nèi)存:
一個(gè)是在清單文件中的Application下添加largeHeap="true"這個(gè)屬性,另一個(gè)就是同一個(gè)應(yīng)用開(kāi)啟多個(gè)進(jìn)程來(lái)擴(kuò)大一個(gè)應(yīng)用的總內(nèi)存空間。
第二種方法其實(shí)就很常見(jiàn)了,比方說(shuō)我使用過(guò)個(gè)推的SDK,個(gè)推的Service其實(shí)就是處在另外一個(gè)單獨(dú)的進(jìn)程中。
內(nèi)存泄漏(多方向討論):
靜態(tài)變量導(dǎo)致的內(nèi)存泄漏
辦法:將內(nèi)部類(lèi)設(shè)為靜態(tài)內(nèi)部類(lèi)或獨(dú)立出來(lái);使用context.getApplicationContext()。
單例模式導(dǎo)致的內(nèi)存泄漏
方案:傳參context.getApplicationContext()。
屬性動(dòng)畫(huà)導(dǎo)致的內(nèi)存泄漏
方案:在Activity.onDestroy()中調(diào)用Animator.cancel()停止動(dòng)畫(huà)。
Handler導(dǎo)致的內(nèi)存泄漏
方案:使用靜態(tài)內(nèi)部類(lèi)+WeakReference弱引用;當(dāng)外部類(lèi)結(jié)束生命周期時(shí)清空消息隊(duì)列。
線程導(dǎo)致的內(nèi)存泄漏
方案:將AsyncTask和Runnable設(shè)為靜態(tài)內(nèi)部類(lèi)或獨(dú)立出來(lái);在線程內(nèi)部采用弱引用保存Context引用。
資源未關(guān)閉導(dǎo)致的內(nèi)存泄漏
方案:在Activity銷(xiāo)毀的時(shí)候要及時(shí)關(guān)閉或者注銷(xiāo)。例如:
① BraodcastReceiver:調(diào)用unregisterReceiver()注銷(xiāo);
②Cursor,Stream、File:調(diào)用close()關(guān)閉;
③Bitmap:調(diào)用recycle()釋放內(nèi)存(2.3版本后無(wú)需手動(dòng))。
Adapter導(dǎo)致的內(nèi)存泄漏
方案:在構(gòu)造Adapter時(shí)使用緩存的convertView。
WebView導(dǎo)致的內(nèi)存泄漏。
方案:其實(shí)避免WebView導(dǎo)致內(nèi)存泄漏的最好方法就是讓W(xué)ebView所在的Activity處于另一個(gè)進(jìn)程中,當(dāng)這個(gè)Activity結(jié)束時(shí)殺死當(dāng)前WebView所處的進(jìn)程即可,我記得阿里釘釘?shù)腤ebView就是另外開(kāi)啟的一個(gè)進(jìn)程,應(yīng)該也是采用這種方法避免內(nèi)存泄漏。
集合類(lèi)泄漏
方案:在onDestry時(shí)回收不需要的集合。
PS:為什么會(huì)導(dǎo)致泄漏,以及泄漏的具體情況,更多原理,后文另外有專(zhuān)門(mén)的分析整理。
卡頓優(yōu)化
本質(zhì):優(yōu)化UI、提高啟動(dòng)跳轉(zhuǎn)還有響應(yīng)的速度。
常用方案:
不在主線程進(jìn)行網(wǎng)絡(luò)訪問(wèn)/大文件的IO操作
繪制UI盡量減少繪制UI層次;減少不必要的view嵌套,可以用Hierarchy Viewer工具來(lái)檢測(cè),后面會(huì)詳細(xì)講;
當(dāng)布局是用的FrameLayout,可以把它改成merge,可以避免自己的幀布局和系統(tǒng)的ContentFrameLayout幀布局重疊造成重復(fù)計(jì)算(measure和layout)
提高顯示速度,使用ViewStub:當(dāng)加載的時(shí)候才會(huì)占用。不加載的時(shí)候就是隱藏的,僅僅占用位置。
在view層級(jí)相同的情況下,盡量使用 LinerLayout而不是RelativeLayout;因?yàn)镽elativeLayout在測(cè)量的時(shí)候會(huì)測(cè)量二次,而LinerLayout測(cè)量一次,可以看下它們的源碼;
刪除控件中無(wú)用的屬性;
布局復(fù)用.比如listView 布局復(fù)用
盡量避免過(guò)度繪制(overdraw),比如:背景經(jīng)常容易造成過(guò)度繪制。由于我們布局設(shè)置了背景,同時(shí)用到的MaterialDesign的主題會(huì)默認(rèn)給一個(gè)背景。這時(shí)應(yīng)該把主題添加的背景去掉;還有移除
XML 中非必須的背景
自定義View優(yōu)化。使用 canvas.clipRect()來(lái)幫助系統(tǒng)識(shí)別那些可見(jiàn)的區(qū)域,只有在這個(gè)區(qū)域內(nèi)才會(huì)被繪制。也是避免過(guò)度繪制.
啟動(dòng)優(yōu)化,啟動(dòng)速度的監(jiān)控,發(fā)現(xiàn)影響啟動(dòng)速度的問(wèn)題所在,優(yōu)化啟動(dòng)邏輯,提高應(yīng)用的啟動(dòng)速度。比如閃屏頁(yè)面,合理優(yōu)化布局,加載邏輯優(yōu)化,數(shù)據(jù)準(zhǔn)備.
合理的刷新機(jī)制,盡量減少刷新次數(shù),盡量避免后臺(tái)有高的 CPU 線程運(yùn)行,縮小刷新區(qū)域。
耗電優(yōu)化
本質(zhì):減少電量消耗。
常用方案:
合理的使用wake_lock鎖,wake_lock鎖主要是相對(duì)系統(tǒng)的休眠(這里就是為了省電,才做休)而言的,意思就是我的程序給CPU加了這個(gè)鎖那系統(tǒng)就不會(huì)休眠了,這樣做的目的是為了全力配合我們程序的運(yùn)行。有的情況如果不這么做就會(huì)出現(xiàn)一些問(wèn)題,比如微信等及時(shí)通訊的心跳包會(huì)在熄屏不久后停止網(wǎng)絡(luò)訪問(wèn)等問(wèn)題。所以微信里面是有大量使用到了wake_lock鎖。
使用jobScheduler2,集中處理一些網(wǎng)絡(luò)請(qǐng)求,有些不用很及時(shí)的處理可以放在充電的時(shí)候處理,比如,圖片的處理,APP下載更新等等;
計(jì)算優(yōu)化,避開(kāi)浮點(diǎn)運(yùn)算等。
數(shù)據(jù)在網(wǎng)絡(luò)上傳輸時(shí),盡量壓縮數(shù)據(jù)后再傳輸,建議用FlatBuffer序列化技術(shù),這個(gè)比json效率高很多倍,不了解FlatBuffer,建議找資料學(xué)習(xí)一下。
理論方面
Android的性能優(yōu)化牽扯的知識(shí)點(diǎn)很多,除了上面講過(guò)的這些常用解決方案,底層原理也值得我們深入探討,此外還有性能監(jiān)控還有工具的使用。
一篇文章難以詳盡,我根據(jù)自己多年的Android開(kāi)發(fā)經(jīng)驗(yàn)把這些性能優(yōu)化的底層原理還有各種問(wèn)題的解決方案等都整理成了PDF文檔。
大家可以通過(guò)掃描下方二維碼找我助手免費(fèi)領(lǐng)取。
項(xiàng)目實(shí)戰(zhàn)
除了理論部分,這邊還給大家整理了一份各大廠的Android性能優(yōu)化實(shí)戰(zhàn)案例,里面詳細(xì)的向大家介紹了互聯(lián)網(wǎng)巨頭的性能優(yōu)化方案。
PS:本資料包含且不限于騰訊、愛(ài)奇藝、字節(jié)跳動(dòng)、百度、京東、支付寶、搜狐、攜程、谷歌、網(wǎng)易、高德等,幾乎囊括了所有互聯(lián)網(wǎng)大廠。
《性能優(yōu)化知識(shí)技能手冊(cè)》理論總結(jié)配合《大廠性能優(yōu)化項(xiàng)目實(shí)戰(zhàn)》分析一起食用,效果更好哦~

感興趣的朋友可以通過(guò)掃描下方二維碼找我助手免費(fèi)領(lǐng)取。
