??使用過(guò)Android系統(tǒng)手機(jī)的同學(xué)都知道,Android手機(jī)越用越卡,這個(gè)卡主要體現(xiàn)在手機(jī)系統(tǒng)越用越卡,打開(kāi)APP的速度越來(lái)越慢丘逸。Android手機(jī)越用越卡的原因主要有:1挽放、Android系統(tǒng)源碼是開(kāi)放的绍赛,像國(guó)內(nèi)的幾大手機(jī)廠商,都是對(duì)系統(tǒng)進(jìn)入定制開(kāi)發(fā)辑畦,這樣就會(huì)引發(fā)一系列問(wèn)題吗蚌,比如說(shuō)著名的系統(tǒng)碎片化問(wèn)題;2纯出、APP開(kāi)發(fā)人員要對(duì)各個(gè)系統(tǒng)做各種適配蚯妇,開(kāi)發(fā)人員的水平參次不齊,開(kāi)發(fā)出來(lái)的APP就會(huì)出現(xiàn)這樣那樣的問(wèn)題暂筝。
??Android應(yīng)用的性能優(yōu)化是每個(gè)Android開(kāi)發(fā)人員必然會(huì)遇到的箩言,也是跳槽面試時(shí)基本必問(wèn)的問(wèn)題。下面是筆者總結(jié)的一些APP性能優(yōu)化原則焕襟,如果能遵循下面這些優(yōu)化原則陨收,那么開(kāi)發(fā)出來(lái)的APP肯定會(huì)更流暢一點(diǎn)、用戶體驗(yàn)更好一點(diǎn)鸵赖、更穩(wěn)定一點(diǎn)务漩。
1.布局優(yōu)化
??思想概述: 盡量減少布局文件的層次(android繪制時(shí)的工作量減小,性能提高)它褪,避免過(guò)渡繪制饵骨。
??首先刪除布局中無(wú)用的控件和層級(jí),其次有選擇地使用性能較低的ViewGroup茫打,比如LinearLayout居触。如果布局中有的布局既可以用LinearLayout也可以用RelativeLayout,那就用LinearLayout老赤,這是因?yàn)镽elativeLayout比較復(fù)雜轮洋,他的布局過(guò)程花費(fèi)更多的CPU時(shí)間。FrameLayout和LinearLayout一樣都是一種簡(jiǎn)單高效的ViewGroup抬旺,因此可以考慮使用他們弊予,但是很多時(shí)候,單純的通過(guò)一個(gè)LinearLayout或者FrameLayout無(wú)法實(shí)現(xiàn)產(chǎn)品的效果嚷狞,需要通過(guò)嵌套的方式來(lái)完成块促,這種情況建議采用RelativeLayout荣堰,因?yàn)閂iewGroup的嵌套就相當(dāng)于增加了布局的層級(jí)床未,同樣會(huì)降低程序的性能。
??布局優(yōu)化的另一種手段是采用<merge>標(biāo)簽振坚、<include>標(biāo)簽和ViewStub:
- <merge>標(biāo)簽:如果當(dāng)前布局和包含的布局中都是豎直方向薇搁,那么使用<merge>標(biāo)簽可以去掉多余的LinearLayout,一般和<include>標(biāo)簽一起使用渡八,從而減少布局的層級(jí)啃洋;
- <include>標(biāo)簽:布局重用传货,不用把已經(jīng)寫過(guò)的布局重新寫一遍,代碼的復(fù)用宏娄;
- ViewStub:繼承view问裕,非常輕量級(jí)且寬高都是0,自己不參加任何布局的繪制過(guò)程孵坚,提供了按需加載功能粮宛,當(dāng)需要時(shí)才將ViewStub中的布局加載到內(nèi)存,這提高了程序的初始化效率卖宠。
2.繪制優(yōu)化
??思想概述:View的onDraw方法避免大量的操作巍杈,onDraw方法中不要?jiǎng)?chuàng)建新的局部對(duì)象(onDraw方法可能被頻繁調(diào)用,會(huì)產(chǎn)生大量的臨時(shí)對(duì)象扛伍,導(dǎo)致系統(tǒng)更加頻繁的gc筷畦,降低程序的執(zhí)行效率),onDraw方法中不要做耗時(shí)的任務(wù)刺洒,不能執(zhí)行成千上萬(wàn)次的循環(huán)操作(搶占cpu的時(shí)間片鳖宾,造成View繪制過(guò)程不夠流暢,每幀的繪制時(shí)間不超過(guò)16ms)作媚。
3.內(nèi)存泄露優(yōu)化
??內(nèi)存泄露的優(yōu)化的思路主要分為兩個(gè)方面:一方面是在開(kāi)發(fā)過(guò)程中避免寫出內(nèi)存泄露的代碼攘滩,另一方面通過(guò)一些分析工具比如MAT、LeakCanry或Android Profiler等來(lái)找出潛在的內(nèi)存泄露繼而解決纸泡。
??常見(jiàn)的內(nèi)存泄露舉例:
- Context使用不當(dāng)造成內(nèi)存泄露:不要對(duì)一個(gè)Activity漂问、 Context保持長(zhǎng)生命周期的引用,盡量在一切可以使用應(yīng)用ApplicationContext代替Context的地方進(jìn)行替換)女揭;
- 靜態(tài)變量導(dǎo)致內(nèi)存泄露:如果我們將activity的context對(duì)象賦值給activity的全局的靜態(tài)變量蚤假。那么就會(huì)造成activity無(wú)法正常銷毀,因?yàn)殪o態(tài)變量在引用它吧兔;
- 單例模式導(dǎo)致的內(nèi)存泄漏:如果我們?cè)趩卫J街袑?shí)現(xiàn)了觀察者模式磷仰,監(jiān)聽(tīng)實(shí)現(xiàn)了接口的對(duì)象,當(dāng)我們?cè)贏ctivity中注冊(cè)了監(jiān)聽(tīng)方法以后境蔼,而沒(méi)有進(jìn)行解注冊(cè),就會(huì)引起內(nèi)存泄漏灶平;在單例模式初始化是,如果傳入Activity或Context實(shí)例箍土,也會(huì)引起內(nèi)存泄露逢享;
- 屬性動(dòng)畫(huà)導(dǎo)致的內(nèi)存泄露:屬性動(dòng)畫(huà)中有一類無(wú)限循環(huán)的動(dòng)畫(huà),如果在Activity中播放此類動(dòng)畫(huà)而沒(méi)有在onDestory中停止動(dòng)畫(huà)吴藻,那么動(dòng)畫(huà)會(huì)一直播放下去瞒爬,盡管無(wú)法在界面上看到動(dòng)畫(huà)效果了,而且這個(gè)時(shí)候,Activity的View會(huì)被動(dòng)畫(huà)持有侧但,而View有持有了Activity矢空,最終導(dǎo)致Activity無(wú)法釋放;
- 警惕線程未終止造成的內(nèi)存泄露:譬如在Activity中關(guān)聯(lián)了一個(gè)生命周期超過(guò)Activity的Thread禀横,在退出Activity時(shí)切記要結(jié)束線程屁药。一個(gè)典型的例子就是HandlerThread的run方法是一個(gè)死循環(huán),它不會(huì)自己結(jié)束柏锄,線程的生命周期超過(guò)了Activity生命周期者祖,我們必須手動(dòng)在Activity的銷毀方法中調(diào)用handlerThread.quit()才不會(huì)泄露;
- 對(duì)象的注冊(cè)與反注冊(cè)沒(méi)有成對(duì)出現(xiàn)造成的內(nèi)存泄露绢彤;譬如注冊(cè)廣播接收器七问、注冊(cè)觀察者(典型的譬如數(shù)據(jù)庫(kù)的監(jiān)聽(tīng))等;
- 創(chuàng)建與關(guān)閉沒(méi)有成對(duì)出現(xiàn)造成的泄露茫舶;如Cursor資源必須手動(dòng)關(guān)閉械巡,WebView必須手動(dòng)銷毀,流等對(duì)象必須手動(dòng)關(guān)閉等饶氏;
- 非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例容易造成內(nèi)存泄漏讥耗;即一個(gè)類中如果你不能夠控制它其中內(nèi)部類的生命周期(譬如Activity中的一些特殊Handler等),則盡量使用靜態(tài)類和弱引用來(lái)處理(譬如ViewRoot的實(shí)現(xiàn))疹启;
- 不要在執(zhí)行頻率很高的方法或者循環(huán)中創(chuàng)建對(duì)象古程,可以使用HashTable等創(chuàng)建一組對(duì)象容器從容器中取那些對(duì)象,而不用每次new與釋放喊崖;
- 注意WebView的泄漏:Android中的WebView存在很大的兼容性問(wèn)題挣磨,解決方法是:讓onDetachedFromWindow先走,在主動(dòng)調(diào)用destroy()之前荤懂,把webview從它的parent上面移除掉茁裙,具體代碼如下:
@Override
protected void onDestroy() {
if( mWebView!=null) {
// 如果先調(diào)用destroy()方法,則會(huì)命中if (isDestroyed()) return;這一行代碼节仿,需要先onDetachedFromWindow()晤锥,再
// destory()
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.stopLoading();
// 退出時(shí)調(diào)用此方法,移除綁定的服務(wù)廊宪,否則某些特定系統(tǒng)會(huì)報(bào)錯(cuò)
mWebView.getSettings().setJavaScriptEnabled(false);
mWebView.clearHistory();
mWebView.clearView();
mWebView.removeAllViews();
mWebView.destroy();
}
super.on Destroy();
}
PS:內(nèi)存泄露和內(nèi)存溢出OOM的關(guān)系:內(nèi)存泄露小范圍積累不能釋放會(huì)導(dǎo)致卡頓矾瘾,內(nèi)存泄露長(zhǎng)期不能釋放,累積超過(guò)閾值會(huì)引起OOM箭启,如果瞬間申請(qǐng)的內(nèi)存超過(guò)應(yīng)用允許的閾值也會(huì)引起OOM(如應(yīng)用的某些邏輯操作瘋狂的消耗掉大量?jī)?nèi)存(譬如加載一張不經(jīng)過(guò)處理的超大超高清圖片等)導(dǎo)致超過(guò)閾值引起OOM)壕翩,可見(jiàn)兩者是交集關(guān)系。內(nèi)存溢出(OutOfMemoryError)的核心原因就是應(yīng)用的內(nèi)存超過(guò)閾值了册烈。
4.響應(yīng)速度優(yōu)化
??響應(yīng)速度優(yōu)化的核心是避免在主線程中做耗時(shí)操作戈泼,而是將這些耗時(shí)操作放在子線程中去執(zhí)行,即采用異步的方式去執(zhí)行赏僧。響應(yīng)速度過(guò)慢體現(xiàn)在Activity的啟動(dòng)畫(huà)面上大猛,如果在主線程中做太多的事情,會(huì)導(dǎo)致Activity啟動(dòng)時(shí)出現(xiàn)黑屏的現(xiàn)象淀零,甚至出現(xiàn)ANR挽绩。當(dāng)發(fā)生了ANR以后。系統(tǒng)會(huì)在/data/anr目錄創(chuàng)建一個(gè)文件traces.txt(PS:不同的手機(jī)存儲(chǔ)的路徑可能會(huì)有不同)驾中,通過(guò)分析trace文件唉堪,可以進(jìn)一步定位ANR的原因。
5.ListView的優(yōu)化
??ListView的優(yōu)化很簡(jiǎn)單肩民,其思想概述:首先采用ViewHolder并避免在getView中執(zhí)行耗時(shí)操作唠亚;其次要根據(jù)列表的滑動(dòng)狀態(tài)來(lái)控制任務(wù)的執(zhí)行頻率,比如當(dāng)列表快速滑動(dòng)時(shí)顯然是不太適合開(kāi)啟大量的異步任務(wù)持痰;最后灶搜,可以嘗試開(kāi)啟硬件加速來(lái)使ListView的滑動(dòng)更加流暢。
??ListView的優(yōu)化現(xiàn)在最流行的方式是用RecyclerView代替工窍,若用RecyclerView代替ListView顯示列表數(shù)據(jù)割卖,可以忽略該原則。
6.Bitmap優(yōu)化
??Bitmap優(yōu)化同樣比較簡(jiǎn)單患雏,其思想概述:1鹏溯、通過(guò)BitMapFactory.Options來(lái)根據(jù)需要對(duì)圖片進(jìn)行采樣,采樣過(guò)程主要用到了BitmapFactory.Options的inSampleSize參數(shù)淹仑;2丙挽、利用inBitmap的高級(jí)特性提高Android系統(tǒng)在Bitmap分配與釋放執(zhí)行效率上的提升,使用該變量可以復(fù)用舊的Bitmap的內(nèi)存而不用重新分配以及銷毀舊Bitmap匀借,進(jìn)而改善運(yùn)行效率取试,即復(fù)用了內(nèi)存減少了內(nèi)存占用(只要新的Bitmap的內(nèi)存小于舊Bitmap的內(nèi)存大小,即可進(jìn)行復(fù)用的操作怀吻;Glide內(nèi)部也使用了inBitmap作為緩存復(fù)用的一種方式瞬浓。)。
7.線程優(yōu)化
??思想概述:采用線程池蓬坡,避免程序中存在大量的Thread猿棉。線程池可以重用內(nèi)部的線程,從而避免了線程的創(chuàng)建和銷毀所帶來(lái)的性能開(kāi)銷屑咳。同時(shí)線程池還能有效的控制線程池的最大并發(fā)數(shù)萨赁,避免大量的線程互相搶占系統(tǒng)資源從而導(dǎo)致阻塞現(xiàn)象發(fā)生。
8.其他性能優(yōu)化原則
- 避免創(chuàng)建過(guò)多的對(duì)象兆龙;
- 不要過(guò)多的使用枚舉杖爽,因?yàn)槊杜e占用的內(nèi)存空間要比標(biāo)準(zhǔn)類型大敲董;
- 常量使用static final修飾;
- 采用內(nèi)存緩存和磁盤緩存慰安;
- 使用更小的圖片:盡量使用更小的圖片資源腋寨;
- 采用靜態(tài)內(nèi)部類,這樣可以避免潛在的由于內(nèi)部類而導(dǎo)致的內(nèi)存泄漏化焕;
- 用好Lint工具:AndroidStudio自帶的Lint工具可以檢查出APP代碼里面潛在的更多的優(yōu)化建議萄窜;
- 使用ProGuard來(lái)剔除不需要的代碼:ProGuard能夠通過(guò)移除不需要的代碼,重命名類撒桨,域與方法等等對(duì)代碼進(jìn)行壓縮查刻,優(yōu)化與混淆。使用ProGuard可以使得你的代碼更加緊湊凤类,這樣能夠減少mapping代碼所需要的內(nèi)存空間穗泵。
??如果感覺(jué)我的文章對(duì)您有所幫助,麻煩動(dòng)動(dòng)小手給個(gè)喜歡谜疤,如果有疑問(wèn)火欧,也歡迎在下方留言,謝謝>ソ亍N帧!