目錄介紹
- 1.OOM和崩潰優(yōu)化
- 1.1 OOM優(yōu)化
- 1.2 ANR優(yōu)化
- 1.3 Crash優(yōu)化
- 2.內(nèi)存泄漏優(yōu)化
- 2.0 動(dòng)畫資源未釋放
- 2.1 錯(cuò)誤使用單利
- 2.2 錯(cuò)誤使用靜態(tài)變量
- 2.3 handler內(nèi)存泄漏
- 2.4 線程造成內(nèi)存泄漏
- 2.5 非靜態(tài)內(nèi)部類
- 2.6 未移除監(jiān)聽
- 2.7 持有activity引用
- 2.8 資源未關(guān)閉
- 2.9 其他原因
- 3.布局優(yōu)化
- 3.1 include優(yōu)化
- 3.2 ViewStub優(yōu)化
- 3.3 merge優(yōu)化
- 3.4 其他建議
- 4.代碼優(yōu)化
- 4.1 lint代碼檢測(cè)
- 4.2 代碼規(guī)范優(yōu)化
- 4.3 View異常優(yōu)化
- 4.4 去除淡黃色警告優(yōu)化
- 4.5 合理使用集合
- 4.6 Activity不可見優(yōu)化
- 4.7 節(jié)制的使用Service
- 5.網(wǎng)絡(luò)優(yōu)化
- 5.1 圖片分類
- 5.2 獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化
- 5.3 網(wǎng)絡(luò)請(qǐng)求異常攔截優(yōu)化
- 6.線程優(yōu)化
- 6.1 使用線程池
- 7.圖片優(yōu)化
- 7.1 bitmap優(yōu)化
- 7.2 glide加載優(yōu)化
- 8.加載優(yōu)化
- 8.1 懶加載優(yōu)化
- 8.2 啟動(dòng)頁(yè)優(yōu)化
- 9.其他優(yōu)化
- 9.1 靜態(tài)變量?jī)?yōu)化
- 9.2 注解替代枚舉
- 9.3 多渠道打包優(yōu)化
- 9.4 TrimMemory和LowMemory優(yōu)化
- 9.5 輪詢操作優(yōu)化
- 9.6 去除重復(fù)依賴庫(kù)優(yōu)化
- 9.7 四種引用優(yōu)化
- 9.8 加載loading優(yōu)化
- 9.9 對(duì)象池Pools優(yōu)化
- 10.RecyclerView優(yōu)化
- 10.1 頁(yè)面為何卡頓
- 10.2 具體優(yōu)化方案
好消息
- 博客筆記大匯總【16年3月到至今】逮京,包括Java基礎(chǔ)及深入知識(shí)點(diǎn)草描,Android技術(shù)博客妻导,Python學(xué)習(xí)筆記等等术浪,還包括平時(shí)開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正鲤孵,持續(xù)完善……開源的文件是markdown格式的!同時(shí)也開源了生活博客,從12年起廊散,積累共計(jì)N篇[近100萬(wàn)字,陸續(xù)搬到網(wǎng)上]缭受,轉(zhuǎn)載請(qǐng)注明出處,謝謝胰丁!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好妻顶,可以star一下幔嗦,謝謝!當(dāng)然也歡迎提出建議汇恤,萬(wàn)事起于忽微,量變引起質(zhì)變!
1.OOM和崩潰優(yōu)化
1.2 ANR優(yōu)化
- ANR的產(chǎn)生需要滿足三個(gè)條件
- 主線程:只有應(yīng)用程序進(jìn)程的主線程響應(yīng)超時(shí)才會(huì)產(chǎn)生ANR匠璧;
- 超時(shí)時(shí)間:產(chǎn)生ANR的上下文不同媳维,超時(shí)時(shí)間也會(huì)不同执虹,但只要在這個(gè)時(shí)間上限內(nèi)沒有響應(yīng)就會(huì)ANR当叭;
- 輸入事件/特定操作:輸入事件是指按鍵磺芭、觸屏等設(shè)備輸入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各個(gè)函數(shù),產(chǎn)生ANR的上下文不同间螟,導(dǎo)致ANR的原因也會(huì)不同;
- ANR優(yōu)化具體措施
- 將所有耗時(shí)操作,比如訪問網(wǎng)絡(luò)见坑,Socket通信,查詢大量SQL 語(yǔ)句,復(fù)雜邏輯計(jì)算等都放在子線程中去孙蒙,然
后通過(guò)handler.sendMessage、runonUIThread坦胶、AsyncTask 等方式更新UI。無(wú)論如何都要確保用戶界面作的流暢
度凑队。如果耗時(shí)操作需要讓用戶等待,那么可以在界面上顯示度條叫惊。 - 使用AsyncTask處理耗時(shí)IO操作缓待。在一些同步的操作主線程有可能被鎖步悠,需要等待其他線程釋放相應(yīng)鎖才能繼續(xù)執(zhí)行,這樣會(huì)有一定的ANR風(fēng)險(xiǎn)谚咬,對(duì)于這種情況有時(shí)也可以用異步線程來(lái)執(zhí)行相應(yīng)的邏輯郎嫁。另外尚辑,要避免死鎖的發(fā)生月褥。
- 使用Handler處理工作線程結(jié)果灯荧,而不是使用Thread.wait()或者Thread.sleep()來(lái)阻塞主線程。
- Activity的onCreate和onResume回調(diào)中盡量避免耗時(shí)的代碼
- BroadcastReceiver中onReceive代碼也要盡量減少耗時(shí),建議使用IntentService處理擦秽。
- 各個(gè)組件的生命周期函數(shù)都不應(yīng)該有太耗時(shí)的操作越败,即使對(duì)于后臺(tái)Service或者ContentProvider來(lái)講究飞,應(yīng)用在后臺(tái)運(yùn)行時(shí)候其onCreate()時(shí)候不會(huì)有用戶輸入引起事件無(wú)響應(yīng)ANR,但其執(zhí)行時(shí)間過(guò)長(zhǎng)也會(huì)引起Service的ANR和ContentProvider的ANR
- 將所有耗時(shí)操作,比如訪問網(wǎng)絡(luò)见坑,Socket通信,查詢大量SQL 語(yǔ)句,復(fù)雜邏輯計(jì)算等都放在子線程中去孙蒙,然
2.內(nèi)存泄漏優(yōu)化
- 內(nèi)存檢測(cè)第一種:代碼方式獲取內(nèi)存
/** * 內(nèi)存使用檢測(cè):可以調(diào)用系統(tǒng)的getMemoryInfo()來(lái)獲取當(dāng)前內(nèi)存的使用情況 */ private void initMemoryInfo() { ActivityManager activityManager = (ActivityManager) Utils.getApp() .getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); if (activityManager != null) { activityManager.getMemoryInfo(memoryInfo); LogUtils.d("totalMem=" + memoryInfo.totalMem + ",availMem=" + memoryInfo.availMem); if (!memoryInfo.lowMemory) { // 運(yùn)行在低內(nèi)存環(huán)境 } } }
- 內(nèi)存檢測(cè)第二種:leakcanary工具
- LeakCanary的原理是監(jiān)控每個(gè)activity,在activity ondestory后奔穿,在后臺(tái)線程檢測(cè)引用男摧,然后過(guò)一段時(shí)間進(jìn)行g(shù)c拇颅,gc后如果引用還在,那么dump出內(nèi)存堆棧,并解析進(jìn)行可視化顯示。
2.0 動(dòng)畫資源未釋放
- 問題代碼
public class LeakActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); textView = (TextView)findViewById(R.id.text_view); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"rotation",0,360); objectAnimator.setRepeatCount(ValueAnimator.INFINITE); objectAnimator.start(); } }
- 解決辦法
- 在屬性動(dòng)畫中有一類無(wú)限循環(huán)動(dòng)畫,如果在Activity中播放這類動(dòng)畫并且在onDestroy中去停止動(dòng)畫打月,那么這個(gè)動(dòng)畫將會(huì)一直播放下去鱼冀,這時(shí)候Activity會(huì)被View所持有千绪,從而導(dǎo)致Activity無(wú)法被釋放充易。解決此類問題則是需要早Activity中onDestroy去去調(diào)用objectAnimator.cancel()來(lái)停止動(dòng)畫。
@Override protected void onDestroy() { super.onDestroy(); mAnimator.cancel(); }
2.1 錯(cuò)誤使用單利
- 在開發(fā)中單例經(jīng)常需要持有Context對(duì)象荸型,如果持有的Context對(duì)象生命周期與單例生命周期更短時(shí)盹靴,或?qū)е翪ontext無(wú)法被釋放回收,則有可能造成內(nèi)存泄漏瑞妇。比如:在一個(gè)Activity中調(diào)用的悬钳,然后關(guān)閉該Activity則會(huì)出現(xiàn)內(nèi)存泄漏。
- 解決辦法:
- 要保證Context和AppLication的生命周期一樣,修改后代碼如下:
- this.mContext = context.getApplicationContext();
- 1望门、如果此時(shí)傳入的是 Application 的 Context祷膳,因?yàn)?Application 的生命周期就是整個(gè)應(yīng)用的生命周期,所以這將沒有任何問題。
- 2咖气、如果此時(shí)傳入的是 Activity 的 Context乳幸,當(dāng)這個(gè) Context 所對(duì)應(yīng)的 Activity 退出時(shí),由于該 Context 的引用被單例對(duì)象所持有兵多,其生命周期等于整個(gè)應(yīng)用程序的生命周期,所以當(dāng)前 Activity 退出時(shí)它的內(nèi)存并不會(huì)被回收慈格,這就造成泄漏了述吸。
2.2 錯(cuò)誤使用靜態(tài)變量
- 使用靜態(tài)方法是十分方便的。但是創(chuàng)建的對(duì)象,建議不要全局化椰棘,全局化的變量必須加上static。全局化后的變量或者對(duì)象會(huì)導(dǎo)致內(nèi)存泄漏河闰!
- 原因分析
- 這里內(nèi)部類AClass隱式的持有外部類Activity的引用,而在Activity的onCreate方法中調(diào)用了。這樣AClass就會(huì)在Activity創(chuàng)建的時(shí)候是有了他的引用谴蔑,而AClass是靜態(tài)類型的不會(huì)被垃圾回收,Activity在執(zhí)行onDestory方法的時(shí)候由于被AClass持有了引用而無(wú)法被回收劫乱,所以這樣Activity就總是被AClass持有而無(wú)法回收造成內(nèi)存泄露搏嗡。
2.3 handler內(nèi)存泄漏
- 造成內(nèi)存泄漏原因分析
- 通過(guò)內(nèi)部類的方式創(chuàng)建mHandler對(duì)象,此時(shí)mHandler會(huì)隱式地持有一個(gè)外部類對(duì)象引用這里就是MainActivity叉橱,當(dāng)執(zhí)行postDelayed方法時(shí),該方法會(huì)將你的Handler裝入一個(gè)Message工腋,并把這條Message推到MessageQueue中姨丈,MessageQueue是在一個(gè)Looper線程中不斷輪詢處理消息,那么當(dāng)這個(gè)Activity退出時(shí)消息隊(duì)列中還有未處理的消息或者正在處理消息擅腰,而消息隊(duì)列中的Message持有mHandler實(shí)例的引用蟋恬,mHandler又持有Activity的引用,所以導(dǎo)致該Activity的內(nèi)存資源無(wú)法及時(shí)回收趁冈,引發(fā)內(nèi)存泄漏歼争。
- 解決Handler內(nèi)存泄露主要2點(diǎn)
- 有延時(shí)消息,要在Activity銷毀的時(shí)候移除Messages監(jiān)聽
- 匿名內(nèi)部類導(dǎo)致的泄露改為匿名靜態(tài)內(nèi)部類渗勘,并且對(duì)上下文或者Activity使用弱引用沐绒。
2.4 線程造成內(nèi)存泄漏
- 早時(shí)期的時(shí)候處理耗時(shí)操作多數(shù)都是采用Thread+Handler的方式,后來(lái)逐步被AsyncTask取代旺坠,直到現(xiàn)在采用RxJava的方式來(lái)處理異步乔遮。
- 造成內(nèi)存泄漏原因分析
- 在處理一個(gè)比較耗時(shí)的操作時(shí),可能還沒處理結(jié)束MainActivity就執(zhí)行了退出操作取刃,但是此時(shí)AsyncTask依然持有對(duì)MainActivity的引用就會(huì)導(dǎo)致MainActivity無(wú)法釋放回收引發(fā)內(nèi)存泄漏蹋肮。
- 解決辦法
- 在使用AsyncTask時(shí),在Activity銷毀時(shí)候也應(yīng)該取消相應(yīng)的任務(wù)AsyncTask.cancel()方法璧疗,避免任務(wù)在后臺(tái)執(zhí)行浪費(fèi)資源坯辩,進(jìn)而避免內(nèi)存泄漏的發(fā)生。
2.5 非靜態(tài)內(nèi)部類
- 非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例造成的內(nèi)存泄漏崩侠。有的時(shí)候我們可能會(huì)在啟動(dòng)頻繁的Activity中漆魔,為了避免重復(fù)創(chuàng)建相同的數(shù)據(jù)資源,可能會(huì)出現(xiàn)這種寫法啦膜。
- 問題代碼
private static TestResource mResource = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mResource == null){ mResource = new TestResource(); } } class TestResource { //里面代碼引用上下文,Activity.this會(huì)導(dǎo)致內(nèi)存泄漏 }
- 解決辦法
- 將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來(lái)封裝成一個(gè)單例淌喻,如果需要使用Context僧家,請(qǐng)按照上面推薦的使用Application 的 Context。
- 分析問題
- 這樣就在Activity內(nèi)部創(chuàng)建了一個(gè)非靜態(tài)內(nèi)部類的單例裸删,每次啟動(dòng)Activity時(shí)都會(huì)使用該單例的數(shù)據(jù)八拱,這樣雖然避免了資源的重復(fù)創(chuàng)建,不過(guò)這種寫法卻會(huì)造成內(nèi)存泄漏,因?yàn)榉庆o態(tài)內(nèi)部類默認(rèn)會(huì)持有外部類的引用肌稻,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個(gè)靜態(tài)的實(shí)例清蚀,該實(shí)例的生命周期和應(yīng)用的一樣長(zhǎng),這就導(dǎo)致了該靜態(tài)實(shí)例一直會(huì)持有該Activity的引用爹谭,導(dǎo)致Activity的內(nèi)存資源不能正臣闲埃回收。
2.6 未移除監(jiān)聽
- 問題代碼
//add監(jiān)聽诺凡,放到集合里面 tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { @Override public void onWindowFocusChanged(boolean b) { //監(jiān)聽view的加載东揣,view加載出來(lái)的時(shí)候,計(jì)算他的寬高等腹泌。 } });
- 解決辦法
//計(jì)算完后嘶卧,一定要移除這個(gè)監(jiān)聽 tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
- 注意事項(xiàng):
tv.setOnClickListener();//監(jiān)聽執(zhí)行完回收對(duì)象,不用考慮內(nèi)存泄漏 tv.getViewTreeObserver().addOnWindowFocusChangeListene,add監(jiān)聽凉袱,放到集合里面芥吟,需要考慮內(nèi)存泄漏
2.7 持有activity引用
2.8 資源未關(guān)閉
- 在使用IO、File流或者Sqlite专甩、Cursor等資源時(shí)要及時(shí)關(guān)閉钟鸵。這些資源在進(jìn)行讀寫操作時(shí)通常都使用了緩沖,如果及時(shí)不關(guān)閉配深,這些緩沖對(duì)象就會(huì)一直被占用而得不到釋放携添,以致發(fā)生內(nèi)存泄露。因此我們?cè)诓恍枰褂盟鼈兊臅r(shí)候就及時(shí)關(guān)閉篓叶,以便緩沖能及時(shí)得到釋放烈掠,從而避免內(nèi)存泄露。
- BroadcastReceiver缸托,ContentObserver左敌,F(xiàn)ileObserver,Cursor俐镐,Callback等在 Activity onDestroy 或者某類生命周期結(jié)束之后一定要 unregister 或者 close 掉矫限,否則這個(gè) Activity 類會(huì)被 system 強(qiáng)引用,不會(huì)被內(nèi)存回收佩抹。值得注意的是叼风,關(guān)閉的語(yǔ)句必須在finally中進(jìn)行關(guān)閉,否則有可能因?yàn)楫惓N搓P(guān)閉資源棍苹,致使activity泄漏无宿。
2.9 其他原因
- 靜態(tài)集合使用不當(dāng)導(dǎo)致的內(nèi)存泄漏
- 有時(shí)候我們需要把一些對(duì)象加入到集合容器(例如ArrayList)中,當(dāng)不再需要當(dāng)中某些對(duì)象時(shí)枢里,如果不把該對(duì)象的引用從集合中清理掉孽鸡,也會(huì)使得GC無(wú)法回收該對(duì)象蹂午。如果集合是static類型的話,那內(nèi)存泄漏情況就會(huì)更為嚴(yán)重彬碱。因此豆胸,當(dāng)不再需要某對(duì)象時(shí),需要主動(dòng)將之從集合中移除巷疼。
- 不需要用的監(jiān)聽未移除會(huì)發(fā)生內(nèi)存泄露
- 問題代碼
//add監(jiān)聽晚胡,放到集合里面 tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { @Override public void onWindowFocusChanged(boolean b) { //監(jiān)聽view的加載,view加載出來(lái)的時(shí)候皮迟,計(jì)算他的寬高等搬泥。 } });
- 解決辦法
//計(jì)算完后,一定要移除這個(gè)監(jiān)聽 tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
- 注意事項(xiàng):
tv.setOnClickListener();//監(jiān)聽執(zhí)行完回收對(duì)象伏尼,不用考慮內(nèi)存泄漏 tv.getViewTreeObserver().addOnWindowFocusChangeListene,add監(jiān)聽忿檩,放到集合里面,需要考慮內(nèi)存泄漏
- 問題代碼
3.布局優(yōu)化
3.1 include優(yōu)化
- 重用布局文件
- 標(biāo)簽可以允許在一個(gè)布局當(dāng)中引入另一個(gè)布局爆阶,那么比如說(shuō)我們程序的所有界面都有一個(gè)公共的部分燥透,這個(gè)時(shí)候最好的做法就是將這個(gè)公共的部分提取到一個(gè)獨(dú)立的布局中,然后每個(gè)界面的布局文件當(dāng)中來(lái)引用這個(gè)公共的布局辨图。
- 如果我們要在標(biāo)簽中覆寫layout屬性班套,必須要將layout_width和layout_height這兩個(gè)屬性也進(jìn)行覆寫,否則覆寫效果將不會(huì)生效故河。
- 標(biāo)簽是作為標(biāo)簽的一種輔助擴(kuò)展來(lái)使用的吱韭,它的主要作用是為了防止在引用布局文件時(shí)引用文件時(shí)產(chǎn)生多余的布局嵌套。布局嵌套越多鱼的,解析起來(lái)就越耗時(shí)理盆,性能就越差。因此編寫布局文件時(shí)應(yīng)該讓嵌套的層數(shù)越少越好凑阶。
- 舉例:比如在LinearLayout里邊使用一個(gè)布局猿规。里邊又有一個(gè)LinearLayout,那么其實(shí)就存在了多余的布局嵌套宙橱,使用merge可以解決這個(gè)問題姨俩。
3.2 ViewStub優(yōu)化
- 僅在需要時(shí)才加載布局[ViewStub]
- 某個(gè)布局當(dāng)中的元素不是一起顯示出來(lái)的,普通情況下只顯示部分常用的元素师郑,而那些不常用的元素只有在用戶進(jìn)行特定操作時(shí)才會(huì)顯示出來(lái)环葵。
- 舉例:填信息時(shí)不是需要全部填的,有一個(gè)添加更多字段的選項(xiàng)宝冕,當(dāng)用戶需要添加其他信息的時(shí)候张遭,才將另外的元素顯示到界面上。用VISIBLE性能表現(xiàn)一般猬仁,可以用ViewStub帝璧。
- ViewStub也是View的一種,但是沒有大小湿刽,沒有繪制功能的烁,也不參與布局,資源消耗非常低诈闺,可以認(rèn)為完全不影響性能渴庆。
- ViewStub所加載的布局是不可以使用標(biāo)簽的,因此這有可能導(dǎo)致加載出來(lái)出來(lái)的布局存在著多余的嵌套結(jié)構(gòu)雅镊。
- 自定義全局的狀態(tài)管理器【充分使用ViewStub】
- 針對(duì)多狀態(tài)襟雷,有數(shù)據(jù),空數(shù)據(jù)仁烹,加載失敗耸弄,加載異常,網(wǎng)絡(luò)異常等卓缰。針對(duì)空數(shù)據(jù)计呈,加載失敗,異常使用viewStub布局征唬,一鍵設(shè)置自定義布局捌显,也是優(yōu)化的一種。
- 項(xiàng)目地址:
3.3 merge優(yōu)化
- 視圖層級(jí)<merge/>
- 這個(gè)標(biāo)簽在UI的結(jié)構(gòu)優(yōu)化中起著非常重要的作用总寒,它可以刪減多余的層級(jí)扶歪,優(yōu)化UI。但是就有一點(diǎn)不好摄闸,無(wú)法預(yù)覽布局效果善镰!
3.4 其他建議
- 減少太多重疊的背景(overdraw)
- 這個(gè)問題其實(shí)最容易解決,建議就是檢查你在布局和代碼中設(shè)置的背景贪薪,有些背景是隱藏在底下的媳禁,它永遠(yuǎn)不可能顯示出來(lái),這種沒必要的背景一定要移除画切,因?yàn)樗芸赡軙?huì)嚴(yán)重影響到app的性能竣稽。如果采用的是selector的背景,將normal狀態(tài)的color設(shè)置為”@android:color/transparent”,也同樣可以解決問題霍弹。
- 避免復(fù)雜的Layout層級(jí)
- 這里的建議比較多一些毫别,首先推薦使用Android提供的布局工具Hierarchy Viewer來(lái)檢查和優(yōu)化布局。第一個(gè)建議是:如果嵌套的線性布局加深了布局層次典格,可以使用相對(duì)布局來(lái)取代岛宦。第二個(gè)建議是:用標(biāo)簽來(lái)合并布局。第三個(gè)建議是:用標(biāo)簽來(lái)重用布局耍缴,抽取通用的布局可以讓布局的邏輯更清晰明了砾肺。記住挽霉,這些建議的最終目的都是使得你的Layout在Hierarchy Viewer里變得寬而淺,而不是窄而深变汪。
- 總結(jié):可以考慮多使用merge和include,ViewStub实胸。盡量使布局淺平番官,根布局盡量少使用RelactivityLayout,因?yàn)镽elactivityLayout每次需要測(cè)量2次。
4.代碼優(yōu)化
- 都是一些微優(yōu)化门躯,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段酷师。
4.1 建議使用lint檢查去除無(wú)效代碼
- lint去除無(wú)效資源和代碼
- 如何檢測(cè)哪些圖片未被使用
- 點(diǎn)擊菜單欄 Analyze -> Run Inspection by Name -> unused resources -> Moudule ‘a(chǎn)pp’ -> OK,這樣會(huì)搜出來(lái)哪些未被使用到未使用到xml和圖片缀遍,如下:
- 如何檢測(cè)哪些無(wú)效代碼
- 使用Android Studio的Lint域醇,步驟:點(diǎn)擊菜單欄 Analyze -> Run Inspection by Name -> unused declaration -> Moudule ‘a(chǎn)pp’ -> OK
- 如何檢測(cè)哪些圖片未被使用
4.2 代碼規(guī)范優(yōu)化
- 避免創(chuàng)建不必要的對(duì)象 不必要的對(duì)象應(yīng)該避免創(chuàng)建:
- 如果有需要拼接的字符串譬挚,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來(lái)進(jìn)行拼接减宣,而不是加號(hào)連接符漆腌,因?yàn)槭褂眉犹?hào)連接符會(huì)創(chuàng)建多余的對(duì)象闷尿,拼接的字符串越長(zhǎng)填具,加號(hào)連接符的性能越低匆骗。
- 當(dāng)一個(gè)方法的返回值是String的時(shí)候,通常需要去判斷一下這個(gè)String的作用是什么描融,如果明確知道調(diào)用方會(huì)將返回的String再進(jìn)行拼接操作的話,可以考慮返回一個(gè)StringBuffer對(duì)象來(lái)代替毛甲,因?yàn)檫@樣可以將一個(gè)對(duì)象的引用進(jìn)行返回,而返回String的話就是創(chuàng)建了一個(gè)短生命周期的臨時(shí)對(duì)象七咧。
- 盡可能地少創(chuàng)建臨時(shí)對(duì)象艾栋,越少的對(duì)象意味著越少的GC操作蝗砾。
- nDraw方法里面不要執(zhí)行對(duì)象的創(chuàng)建
- 靜態(tài)優(yōu)于抽象
- 如果你并不需要訪問一個(gè)對(duì)系那個(gè)中的某些字段悼粮,只是想調(diào)用它的某些方法來(lái)去完成一項(xiàng)通用的功能扣猫,那么可以將這個(gè)方法設(shè)置成靜態(tài)方法,調(diào)用速度提升15%-20%瀑凝,同時(shí)也不用為了調(diào)用這個(gè)方法去專門創(chuàng)建對(duì)象了粤咪,也不用擔(dān)心調(diào)用這個(gè)方法后是否會(huì)改變對(duì)象的狀態(tài)(靜態(tài)方法無(wú)法訪問非靜態(tài)字段)寥枝。
- 對(duì)常量使用static final修飾符
- static int intVal = 42; static String strVal = "Hello, world!";
- 編譯器會(huì)為上面的代碼生成一個(gè)初始方法某筐,稱為方法南誊,該方法會(huì)在定義類第一次被使用的時(shí)候調(diào)用抄囚。這個(gè)方法會(huì)將42的值賦值到intVal當(dāng)中幔托,從字符串常量表中提取一個(gè)引用賦值到strVal上。當(dāng)賦值完成后谬哀,我們就可以通過(guò)字段搜尋的方式去訪問具體的值了玻粪。
- final進(jìn)行優(yōu)化:
- static final int intVal = 42; static final String strVal = "Hello, world!";
- 這樣劲室,定義類就不需要方法了,因?yàn)樗械某A慷紩?huì)在dex文件的初始化器當(dāng)中進(jìn)行初始化喉磁。當(dāng)我們調(diào)用intVal時(shí)可以直接指向42的值协怒,而調(diào)用strVal會(huì)用一種相對(duì)輕量級(jí)的字符串常量方式孕暇,而不是字段搜尋的方式隧哮。
- 這種優(yōu)化方式只對(duì)基本數(shù)據(jù)類型以及String類型的常量有效沮翔,對(duì)于其他數(shù)據(jù)類型的常量是無(wú)效的采蚀。
- 在沒有特殊原因的情況下,盡量使用基本數(shù)據(jù)類型來(lái)代替封裝數(shù)據(jù)類型,int比Integer要更加有效读虏,其它數(shù)據(jù)類型也是一樣盖桥。
- 基本數(shù)據(jù)類型的數(shù)組也要優(yōu)于對(duì)象數(shù)據(jù)類型的數(shù)組。另外兩個(gè)平行的數(shù)組要比一個(gè)封裝好的對(duì)象數(shù)組更加高效忽妒,舉個(gè)例子齿税,F(xiàn)oo[]和Bar[]這樣的數(shù)組凌箕,使用起來(lái)要比Custom(Foo,Bar)[]這樣的一個(gè)數(shù)組高效的多。
4.3 View異常優(yōu)化
- view自定義控件異常銷毀保存狀態(tài)
- 經(jīng)常容易被人忽略芜壁,但是為了追求高質(zhì)量代碼沿盅,這個(gè)也有必要加上韧掩。舉個(gè)例子疗锐!
@Override protected Parcelable onSaveInstanceState() { //異常情況保存重要信息。 //return super.onSaveInstanceState(); final Bundle bundle = new Bundle(); bundle.putInt("selectedPosition",selectedPosition); bundle.putInt("flingSpeed",mFlingSpeed); bundle.putInt("orientation",orientation); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { final Bundle bundle = (Bundle) state; selectedPosition = bundle.getInt("selectedPosition",selectedPosition); mFlingSpeed = bundle.getInt("flingSpeed",mFlingSpeed); orientation = bundle.getInt("orientation",orientation); return; } super.onRestoreInstanceState(state); }
- 經(jīng)常容易被人忽略芜壁,但是為了追求高質(zhì)量代碼沿盅,這個(gè)也有必要加上韧掩。舉個(gè)例子疗锐!
4.4 去除淡黃色警告優(yōu)化
- 淡黃色警告雖然不會(huì)造成崩潰雇卷,但是作為程序員還是要盡量去除淡黃色警告,規(guī)范代碼
4.5 合理使用集合
- 使用優(yōu)化過(guò)的數(shù)據(jù)集合
- Android提供了一系列優(yōu)化過(guò)后的數(shù)據(jù)集合工具類贮折,如SparseArray、SparseBooleanArray每庆、LongSparseArray扣孟,使用這些API可以讓我們的程序更加高效。HashMap工具類會(huì)相對(duì)比較低效利诺,因?yàn)樗枰獮槊恳粋€(gè)鍵值對(duì)都提供一個(gè)對(duì)象入口慢逾,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對(duì)象數(shù)據(jù)類型的時(shí)間口注。
4.6 Activity不可見優(yōu)化
- 當(dāng)Activity界面不可見時(shí)釋放內(nèi)存
- 當(dāng)用戶打開了另外一個(gè)程序寝志,我們的程序界面已經(jīng)不可見的時(shí)候,我們應(yīng)當(dāng)將所有和界面相關(guān)的資源進(jìn)行釋放乐导。重寫Activity的onTrimMemory()方法,然后在這個(gè)方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個(gè)級(jí)別棵磷,一旦觸發(fā)說(shuō)明用戶離開了程序淘太,此時(shí)就可以進(jìn)行資源釋放操作了蒲牧。
- 當(dāng)時(shí)看到這個(gè)覺得很新奇的冰抢,但是具體還是沒有用到翠订,要是那個(gè)大神有具體操作方案尽超,可以分享一下。
4.7 節(jié)制的使用Service
- 節(jié)制的使用Service
- 如果應(yīng)用程序需要使用Service來(lái)執(zhí)行后臺(tái)任務(wù)的話巩踏,只有當(dāng)任務(wù)正在執(zhí)行的時(shí)候才應(yīng)該讓Service運(yùn)行起來(lái)菠净。當(dāng)啟動(dòng)一個(gè)Service時(shí)嗤练,系統(tǒng)會(huì)傾向于將這個(gè)Service所依賴的進(jìn)程進(jìn)行保留,系統(tǒng)可以在LRUcache當(dāng)中緩存的進(jìn)程數(shù)量也會(huì)減少革答,導(dǎo)致切換程序的時(shí)候耗費(fèi)更多性能。我們可以使用IntentService溪食,當(dāng)后臺(tái)任務(wù)執(zhí)行結(jié)束后會(huì)自動(dòng)停止,避免了Service的內(nèi)存泄漏枢析。
5.網(wǎng)絡(luò)優(yōu)化
5.1 圖片分類
- 圖片網(wǎng)絡(luò)優(yōu)化
- 比如我之前看到豆瓣接口,提供一種加載圖片方式特別好把沼。接口返回圖片的數(shù)據(jù)有三種,一種是高清大圖续捂,一種是正常圖片劫拗,一種是縮略小圖页慷。當(dāng)用戶處于wifi下給控件設(shè)置高清大圖,當(dāng)4g或者3g模式下加載正常圖片州袒,當(dāng)弱網(wǎng)條件下加載縮略圖【也稱與加載圖】。
- 簡(jiǎn)單來(lái)說(shuō)根據(jù)用戶的當(dāng)前的網(wǎng)絡(luò)質(zhì)量來(lái)判斷下載什么質(zhì)量的圖片(電商用的比較多)夸研。豆瓣開源接口可以參考一下!
5.2 獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化
- 移動(dòng)端獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化的幾個(gè)點(diǎn)
- 連接復(fù)用:節(jié)省連接建立時(shí)間,如開啟 keep-alive溶握。
- 對(duì)于Android來(lái)說(shuō)默認(rèn)情況下HttpURLConnection和HttpClient都開啟了keep-alive萍肆。只是2.2之前HttpURLConnection存在影響連接池的Bug包雀,具體可見:Android HttpURLConnection及HttpClient選擇
- 請(qǐng)求合并:即將多個(gè)請(qǐng)求合并為一個(gè)進(jìn)行請(qǐng)求葡兑,比較常見的就是網(wǎng)頁(yè)中的CSS Image Sprites讹堤。如果某個(gè)頁(yè)面內(nèi)請(qǐng)求過(guò)多,也可以考慮做一定的請(qǐng)求合并梗醇。
- 減少請(qǐng)求數(shù)據(jù)的大小:對(duì)于post請(qǐng)求,body可以做gzip壓縮的虫溜,header也可以做數(shù)據(jù)壓縮(不過(guò)只支持http
- 返回?cái)?shù)據(jù)的body也可以做gzip壓縮,body數(shù)據(jù)體積可以縮小到原來(lái)的30%左右瘾境。(也可以考慮壓縮返回的json數(shù)據(jù)的key數(shù)據(jù)的體積,尤其是針對(duì)返回?cái)?shù)據(jù)格式變化不大的情況兑凿,支付寶聊天返回的數(shù)據(jù)用到了)
5.3 網(wǎng)絡(luò)請(qǐng)求異常攔截優(yōu)化
- 在獲取數(shù)據(jù)的流程中,訪問接口和解析數(shù)據(jù)時(shí)都有可能會(huì)出錯(cuò)茵瘾,我們可以通過(guò)攔截器在這兩層攔截錯(cuò)誤礼华。
- 1.在訪問接口時(shí),我們不用設(shè)置攔截器拗秘,因?yàn)橐坏┏霈F(xiàn)錯(cuò)誤圣絮,Retrofit會(huì)自動(dòng)拋出異常雕旨。比如扮匠,常見請(qǐng)求異常404捧请,500,503等等棒搜。
- 2.在解析數(shù)據(jù)時(shí)血久,我們?cè)O(shè)置一個(gè)攔截器,判斷Result里面的code是否為成功帮非,如果不成功氧吐,則要根據(jù)與服務(wù)器約定好的錯(cuò)誤碼來(lái)拋出對(duì)應(yīng)的異常。比如末盔,token失效筑舅,禁用同賬號(hào)登陸多臺(tái)設(shè)備,缺少參數(shù)陨舱,參數(shù)傳遞異常等等翠拣。
- 3.除此以外,為了我們要盡量避免在View層對(duì)錯(cuò)誤進(jìn)行判斷游盲,處理误墓,我們必須還要設(shè)置一個(gè)攔截器,攔截onError事件益缎,然后使用ExceptionUtils谜慌,讓其根據(jù)錯(cuò)誤類型來(lái)分別處理。
- 具體可以直接看lib中的ExceptionUtils類莺奔,那么如何調(diào)用呢欣范?入侵性極低,不用改變之前的代碼令哟!
@Override public void onError(Throwable e) { //直接調(diào)用即可 ExceptionUtils.handleException(e); }
6.線程優(yōu)化
6.1 使用線程池
- 將全局線程用線程池管理
- 直接創(chuàng)建Thread實(shí)現(xiàn)runnable方法的弊端
- 大量的線程的創(chuàng)建和銷毀很容易導(dǎo)致GC頻繁的執(zhí)行恼琼,從而發(fā)生內(nèi)存抖動(dòng)現(xiàn)象,而發(fā)生了內(nèi)存抖動(dòng)屏富,對(duì)于移動(dòng)端來(lái)說(shuō)晴竞,最大的影響就是造成界面卡頓
- 線程的創(chuàng)建和銷毀都需要時(shí)間,當(dāng)有大量的線程創(chuàng)建和銷毀時(shí)狠半,那么這些時(shí)間的消耗則比較明顯噩死,將導(dǎo)致性能上的缺失
- 為什么要用線程池
- 重用線程池中的線程,避免頻繁地創(chuàng)建和銷毀線程帶來(lái)的性能消耗典予;有效控制線程的最大并發(fā)數(shù)量甜滨,防止線程過(guò)大導(dǎo)致?lián)屨假Y源造成系統(tǒng)阻塞乐严;可以對(duì)線程進(jìn)行一定地管理瘤袖。
- 使用線程池管理的經(jīng)典例子
- RxJava,RxAndroid昂验,底層對(duì)線程池的封裝管理特別值得參考
- 關(guān)于線程池捂敌,線程艾扮,多線程的具體內(nèi)容
- 參考:輕量級(jí)線程池封裝庫(kù),支持異步回調(diào)占婉,可以檢測(cè)線程執(zhí)行的狀態(tài)
- 該項(xiàng)目中哪里用到頻繁new Thread
- 保存圖片[注意泡嘴,尤其是大圖和多圖場(chǎng)景下注意耗時(shí)太久];某些頁(yè)面從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)逆济;設(shè)置中心清除圖片酌予,視頻,下載文件奖慌,日志抛虫,系統(tǒng)緩存等緩存內(nèi)容
- 使用線程池管理庫(kù)好處,比如保存圖片简僧,耗時(shí)操作放到子線程中建椰,處理過(guò)程中,可以檢測(cè)到執(zhí)行開始岛马,異常棉姐,成功,失敗等多種狀態(tài)啦逆。
- 直接創(chuàng)建Thread實(shí)現(xiàn)runnable方法的弊端
7.圖片優(yōu)化
7.1 bitmap優(yōu)化
- 加載圖片所占的內(nèi)存大小計(jì)算方式
- 加載網(wǎng)絡(luò)圖片:bitmap內(nèi)存大小 = 圖片長(zhǎng)度 x 圖片寬度 x 單位像素占用的字節(jié)數(shù)【看到網(wǎng)上很多都是這樣寫的伞矩,但是不全面】
- 加載本地圖片:bitmap內(nèi)存大小 = width * height * nTargetDensity/inDensity 一個(gè)像素所占的內(nèi)存。注意不要忽略了一個(gè)影響項(xiàng):Density
- 第一種加載圖片優(yōu)化處理:壓縮圖片
- 質(zhì)量壓縮方法:在保持像素的前提下改變圖片的位深及透明度等夏志,來(lái)達(dá)到壓縮圖片的目的扭吁,這樣適合去傳遞二進(jìn)制的圖片數(shù)據(jù),比如分享圖片盲镶,要傳入二進(jìn)制數(shù)據(jù)過(guò)去侥袜,限制500kb之內(nèi)。
- 采樣率壓縮方法:設(shè)置inSampleSize的值(int類型)后溉贿,假如設(shè)為n枫吧,則寬和高都為原來(lái)的1/n,寬高都減少宇色,內(nèi)存降低九杂。
- 縮放法壓縮:Android中使用Matrix對(duì)圖像進(jìn)行縮放、旋轉(zhuǎn)宣蠕、平移例隆、斜切等變換的。功能十分強(qiáng)大抢蚀!
- 第二種加載圖片優(yōu)化:不壓縮加載高清圖片如何做镀层?
- 使用BitmapRegionDecoder,主要用于顯示圖片的某一塊矩形區(qū)域皿曲,如果你需要顯示某個(gè)圖片的指定區(qū)域唱逢,那么這個(gè)類非常合適吴侦。
7.2 glide加載優(yōu)化
- 在畫廊中加載大圖
- 假如你滑動(dòng)特別快,glide加載優(yōu)化就顯得非常重要呢坞古,具體優(yōu)化方法如下所示
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { LoggerUtils.e("initRecyclerView"+ "恢復(fù)Glide加載圖片"); Glide.with(ImageBrowseActivity.this).resumeRequests(); }else { LoggerUtils.e("initRecyclerView"+"禁止Glide加載圖片"); Glide.with(ImageBrowseActivity.this).pauseRequests(); } } });
- 假如你滑動(dòng)特別快,glide加載優(yōu)化就顯得非常重要呢坞古,具體優(yōu)化方法如下所示
8.加載優(yōu)化
8.1 懶加載優(yōu)化
- 該優(yōu)化在新聞?lì)恆pp中十分常見
- ViewPager+Fragment的搭配在日常開發(fā)中也比較常見备韧,可用于切換展示不同類別的頁(yè)面。
- 懶加載,其實(shí)也就是延遲加載,就是等到該頁(yè)面的UI展示給用戶時(shí),再加載該頁(yè)面的數(shù)據(jù)(從網(wǎng)絡(luò)痪枫、數(shù)據(jù)庫(kù)等),而不是依靠ViewPager預(yù)加載機(jī)制提前加載兩三個(gè)织堂,甚至更多頁(yè)面的數(shù)據(jù)。這樣可以提高所屬Activity的初始化速度,也可以為用戶節(jié)省流量.而這種懶加載的方式也已經(jīng)/正在被諸多APP所采用奶陈。
- 具體看這篇文章
8.2 啟動(dòng)頁(yè)優(yōu)化
- 啟動(dòng)時(shí)間分析
- 系統(tǒng)創(chuàng)建進(jìn)程的時(shí)間和應(yīng)用進(jìn)程啟動(dòng)的時(shí)間捧挺,前者是由系統(tǒng)自行完成的,一般都會(huì)很快尿瞭,我們也干預(yù)不了闽烙,我覺得能做的就是去優(yōu)化應(yīng)用進(jìn)程啟動(dòng),具體說(shuō)來(lái)就是從發(fā)Application的onCreate()執(zhí)行開始到MainActivity的onCreate()執(zhí)行結(jié)束這一段時(shí)間声搁。
- 啟動(dòng)時(shí)間優(yōu)化
- Application的onCreate()方法
- MainActivity的onCreate()方法
- 優(yōu)化的手段也無(wú)非三種黑竞,如下所示:
- 延遲初始化
- 后臺(tái)任務(wù)
- 啟動(dòng)界面預(yù)加載
- 啟動(dòng)頁(yè)白屏優(yōu)化
- 為什么存在這個(gè)問題?
- 當(dāng)系統(tǒng)啟動(dòng)一個(gè)APP時(shí)疏旨,zygote進(jìn)程會(huì)首先創(chuàng)建一個(gè)新的進(jìn)程去運(yùn)行這個(gè)APP很魂,但是進(jìn)程的創(chuàng)建是需要時(shí)間的,在創(chuàng)建完成之前檐涝,界面是呈現(xiàn)假死狀態(tài)遏匆,于是系統(tǒng)根據(jù)你的manifest文件設(shè)置的主題顏色的不同來(lái)展示一個(gè)白屏或者黑屏。而這個(gè)黑(白)屏正式的稱呼應(yīng)該是Preview Window谁榜,即預(yù)覽窗口幅聘。
- 實(shí)際上就是是activity默認(rèn)的主題中的android:windowBackground為白色或者黑色導(dǎo)致的。
- 總結(jié)來(lái)說(shuō)啟動(dòng)順序就是:app啟動(dòng)——Preview Window(也稱為預(yù)覽窗口)——啟動(dòng)頁(yè)
- 解決辦法
- 常見有三種窃植,這里解決辦法是給當(dāng)前啟動(dòng)頁(yè)添加一個(gè)有背景的style樣式帝蒿,然后SplashActivity引用當(dāng)前theme主題,注意在該頁(yè)面將window的背景圖設(shè)置為空巷怜!
- 更多關(guān)于啟動(dòng)頁(yè)為什么白屏閃屏葛超,以及不同解決辦法,可以看我這篇博客:App啟動(dòng)頁(yè)面優(yōu)化
- 為什么存在這個(gè)問題?
- 啟動(dòng)時(shí)間優(yōu)化
- IntentService子線程分擔(dān)部分初始化工作
- 現(xiàn)在application初始化內(nèi)容有:阿里云推送初始化延塑,騰訊bugly初始化绣张,im初始化,神策初始化关带,內(nèi)存泄漏工具初始化侥涵,頭條適配方案初始化,阿里云熱修復(fù)……等等。將部分邏輯放到IntentService中處理独令,可以縮短很多時(shí)間端朵。
- 開啟IntentSerVice線程好芭,將部分邏輯和耗時(shí)的初始化操作放到這里處理燃箭,可以減少application初始化時(shí)間
- 關(guān)于IntentService使用和源碼分析,性能分析等可以參考博客:IntentService源碼分析
- IntentService子線程分擔(dān)部分初始化工作
9.其他優(yōu)化
9.1 靜態(tài)變量?jī)?yōu)化
- 盡量不使用靜態(tài)變量保存核心數(shù)據(jù)舍败。這是為什么呢招狸?
- 這是因?yàn)閍ndroid的進(jìn)程并不是安全的,包括application對(duì)象以及靜態(tài)變量在內(nèi)的進(jìn)程級(jí)別變量并不會(huì)一直呆著內(nèi)存里面邻薯,因?yàn)樗苡袝?huì)被kill掉裙戏。
- 當(dāng)被kill掉之后,實(shí)際上app不會(huì)重新開始啟動(dòng)厕诡。Android系統(tǒng)會(huì)創(chuàng)建一個(gè)新的Application對(duì)象累榜,然后啟動(dòng)上次用戶離開時(shí)的activity以造成這個(gè)app從來(lái)沒有被kill掉的假象。而這時(shí)候靜態(tài)變量等數(shù)據(jù)由于進(jìn)程已經(jīng)被殺死而被初始化灵嫌,所以就有了不推薦在靜態(tài)變量(包括Application中保存全局?jǐn)?shù)據(jù)靜態(tài)數(shù)據(jù))的觀點(diǎn)壹罚。
9.2 注解替代枚舉
- 使用注解限定傳入類型
- 比如,尤其是寫第三方開源庫(kù)寿羞,對(duì)于有些暴露給開發(fā)者的方法猖凛,需要限定傳入類型是有必要的。舉個(gè)例子:
- 剛開始的代碼
/** * 設(shè)置播放器類型绪穆,必須設(shè)置 * 注意:感謝某人建議辨泳,這里限定了傳入值類型 * 輸入值:111 或者 222 * @param playerType IjkPlayer or MediaPlayer. */ public void setPlayerType(int playerType) { mPlayerType = playerType; }
- 優(yōu)化后的代碼,有效避免第一種方式開發(fā)者傳入值錯(cuò)誤
/** * 設(shè)置播放器類型玖院,必須設(shè)置 * 注意:感謝某人建議菠红,這里限定了傳入值類型 * 輸入值:ConstantKeys.IjkPlayerType.TYPE_IJK 或者 ConstantKeys.IjkPlayerType.TYPE_NATIVE * @param playerType IjkPlayer or MediaPlayer. */ public void setPlayerType(@ConstantKeys.PlayerType int playerType) { mPlayerType = playerType; } /** * 通過(guò)注解限定類型 * TYPE_IJK IjkPlayer,基于IjkPlayer封裝播放器 * TYPE_NATIVE MediaPlayer难菌,基于原生自帶的播放器控件 */ @Retention(RetentionPolicy.SOURCE) public @interface IjkPlayerType { int TYPE_IJK = 111; int TYPE_NATIVE = 222; } @IntDef({IjkPlayerType.TYPE_IJK,IjkPlayerType.TYPE_NATIVE}) public @interface PlayerType{}
- 使用注解替代枚舉途乃,代碼如下所示
@Retention(RetentionPolicy.SOURCE) public @interface ViewStateType { int HAVE_DATA = 1; int EMPTY_DATA = 2; int ERROR_DATA = 3; int ERROR_NETWORK = 4; }
9.3 多渠道打包優(yōu)化
- 還在手動(dòng)打包嗎?嘗試一下python自動(dòng)化打包吧……
- 瓦力多渠道打包的Python腳本測(cè)試工具扔傅,通過(guò)該自動(dòng)化腳本耍共,自需要run一下或者命令行運(yùn)行腳本即可實(shí)現(xiàn)美團(tuán)瓦力多渠道打包,打包速度很快猎塞。配置信息十分簡(jiǎn)單试读,代碼中已經(jīng)注釋十分詳細(xì)≤ⅲ可以自定義輸出文件路徑钩骇,可以修改多渠道配置信息,簡(jiǎn)單實(shí)用。 項(xiàng)目地址:https://github.com/yangchong211/YCWalleHelper
9.4 TrimMemory和LowMemory優(yōu)化
- 可以優(yōu)化什么倘屹?
- 在 onTrimMemory() 回調(diào)中银亲,應(yīng)該在一些狀態(tài)下清理掉不重要的內(nèi)存資源。對(duì)于這些緩存纽匙,只要是讀進(jìn)內(nèi)存內(nèi)的都算务蝠,例如最常見的圖片緩存、文件緩存等烛缔。拿圖片緩存來(lái)說(shuō)馏段,市場(chǎng)上,常規(guī)的圖片加載庫(kù)践瓷,一般而言都是三級(jí)緩存院喜,所以在內(nèi)存吃緊的時(shí)候,我們就應(yīng)該優(yōu)先清理掉這部分圖片緩存晕翠,畢竟圖片是吃內(nèi)存大戶喷舀,而且再次回來(lái)的時(shí)候,雖然內(nèi)存中的資源被回收掉了淋肾,依然可以從磁盤或者網(wǎng)絡(luò)上恢復(fù)它硫麻。
- 大概的思路如下所示
- 在lowMemory的時(shí)候,調(diào)用Glide.cleanMemory()清理掉所有的內(nèi)存緩存巫员。
- 在App被置換到后臺(tái)的時(shí)候庶香,調(diào)用Glide.cleanMemory()清理掉所有的內(nèi)存緩存。
- 在其它情況的onTrimMemory()回調(diào)中简识,直接調(diào)用Glide.trimMemory()方法來(lái)交給Glide處理內(nèi)存情況赶掖。
9.5 輪詢操作優(yōu)化
- 什么叫輪訓(xùn)請(qǐng)求?
- 簡(jiǎn)單理解就是App端每隔一定的時(shí)間重復(fù)請(qǐng)求的操作就叫做輪訓(xùn)請(qǐng)求七扰,比如:App端每隔一段時(shí)間上報(bào)一次定位信息奢赂,App端每隔一段時(shí)間拉去一次用戶狀態(tài)等,這些應(yīng)該都是輪訓(xùn)請(qǐng)求颈走。比如膳灶,電商類項(xiàng)目,某個(gè)抽獎(jiǎng)活動(dòng)頁(yè)面立由,隔1分鐘調(diào)用一次接口轧钓,彈出一些獲獎(jiǎng)人信息,你應(yīng)該某個(gè)階段看過(guò)這類輪詢操作锐膜!
- 具體優(yōu)化操作
- 長(zhǎng)連接并不是穩(wěn)定的可靠的毕箍,而執(zhí)行輪訓(xùn)操作的時(shí)候一般都是要穩(wěn)定的網(wǎng)絡(luò)請(qǐng)求,而且輪訓(xùn)操作一般都是有生命周期的道盏,即在一定的生命周期內(nèi)執(zhí)行輪訓(xùn)操作而柑,而長(zhǎng)連接一般都是整個(gè)進(jìn)程生命周期的文捶,所以從這方面講也不太適合。
- 建議在service中做輪詢操作媒咳,輪詢請(qǐng)求接口粹排,具體做法和注意要點(diǎn),可以直接看該項(xiàng)目代碼涩澡⊥缍看app包下的LoopRequestService類即可。
- 大概思路:當(dāng)用戶打開這個(gè)頁(yè)面的時(shí)候初始化TimerTask對(duì)象筏养,每個(gè)一分鐘請(qǐng)求一次服務(wù)器拉取訂單信息并更新UI斧抱,當(dāng)用戶離開頁(yè)面的時(shí)候清除TimerTask對(duì)象常拓,即取消輪訓(xùn)請(qǐng)求操作渐溶。
9.6 去除重復(fù)依賴庫(kù)優(yōu)化
- 我相信你看到了這里會(huì)有疑問,網(wǎng)上有許多博客作了這方面說(shuō)明弄抬。但是我在這里想說(shuō)茎辐,如何查找自己項(xiàng)目的所有依賴關(guān)系樹
- 注意要點(diǎn):其中app就是項(xiàng)目mudule名字。 正常情況下就是app掂恕!
gradlew app:dependencies
- 關(guān)于依賴關(guān)系樹的結(jié)構(gòu)圖如下所示拖陆,此處省略很多代碼
| | | | | | \--- android.arch.core:common:1.1.1 (*) | | | | \--- com.android.support:support-annotations:26.1.0 -> 28.0.0 | +--- com.journeyapps:zxing-android-embedded:3.6.0 | | +--- com.google.zxing:core:3.3.2 | | \--- com.android.support:support-v4:25.3.1 | | +--- com.android.support:support-compat:25.3.1 -> 28.0.0 (*) | | +--- com.android.support:support-media-compat:25.3.1 | | | +--- com.android.support:support-annotations:25.3.1 -> 28.0.0 | | | \--- com.android.support:support-compat:25.3.1 -> 28.0.0 (*) | | +--- com.android.support:support-core-utils:25.3.1 -> 28.0.0 (*) | | +--- com.android.support:support-core-ui:25.3.1 -> 28.0.0 (*) | | \--- com.android.support:support-fragment:25.3.1 -> 28.0.0 (*) \--- com.android.support:multidex:1.0.2 -> 1.0.3
- 然后查看哪些重復(fù)jar
- image
- 然后修改gradle配置代碼
api (rootProject.ext.dependencies["zxing"]){ exclude module: 'support-v4' exclude module: 'appcompat-v7' }
9.7 四種引用優(yōu)化
- 軟引用使用場(chǎng)景
-
正常是用來(lái)處理大圖片這種占用內(nèi)存大的情況
- 代碼如下所示
Bitmap bitmap = bitmaps.get(position); //正常是用來(lái)處理圖片這種占用內(nèi)存大的情況 bitmapSoftReference = new SoftReference<>(bitmap); if(bitmapSoftReference.get() != null) { viewHolder.imageView.setImageBitmap(bitmapSoftReference.get()); } //其實(shí)看glide底層源碼可知,也做了相關(guān)軟引用的操作
-
這樣使用軟引用好處
- 通過(guò)軟引用的get()方法懊亡,取得bitmap對(duì)象實(shí)例的強(qiáng)引用依啰,發(fā)現(xiàn)對(duì)象被未回收。在GC在內(nèi)存充足的情況下店枣,不會(huì)回收軟引用對(duì)象速警。此時(shí)view的背景顯示
- 實(shí)際情況中,我們會(huì)獲取很多圖片.然后可能給很多個(gè)view展示, 這種情況下很容易內(nèi)存吃緊導(dǎo)致oom,內(nèi)存吃緊,系統(tǒng)開始會(huì)GC鸯两。這次GC后闷旧,bitmapSoftReference.get()不再返回bitmap對(duì)象,而是返回null钧唐,這時(shí)屏幕上背景圖不顯示忙灼,說(shuō)明在系統(tǒng)內(nèi)存緊張的情況下,軟引用被回收钝侠。
- 使用軟引用以后该园,在OutOfMemory異常發(fā)生之前,這些緩存的圖片資源的內(nèi)存空間可以被釋放掉的帅韧,從而避免內(nèi)存達(dá)到上限里初,避免Crash發(fā)生。
-
正常是用來(lái)處理大圖片這種占用內(nèi)存大的情況
- 弱引用使用場(chǎng)景
- 弱引用–>隨時(shí)可能會(huì)被垃圾回收器回收弱匪,不一定要等到虛擬機(jī)內(nèi)存不足時(shí)才強(qiáng)制回收青瀑。
- 對(duì)于使用頻次少的對(duì)象璧亮,希望盡快回收,使用弱引用可以保證內(nèi)存被虛擬機(jī)回收斥难。比如handler枝嘶,如果希望使用完后盡快回收,看下面代碼
private MyHandler handler = new MyHandler(this); private static class MyHandler extends Handler{ WeakReference<FirstActivity> weakReference; MyHandler(FirstActivity activity) { weakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ } } }
- 到底什么時(shí)候使用軟引用哑诊,什么時(shí)候使用弱引用呢群扶?
- 個(gè)人認(rèn)為,如果只是想避免OutOfMemory異常的發(fā)生镀裤,則可以使用軟引用竞阐。如果對(duì)于應(yīng)用的性能更在意,想盡快回收一些占用內(nèi)存比較大的對(duì)象暑劝,則可以使用弱引用骆莹。
- 還有就是可以根據(jù)對(duì)象是否經(jīng)常使用來(lái)判斷。如果該對(duì)象可能會(huì)經(jīng)常使用的担猛,就盡量用軟引用幕垦。如果該對(duì)象不被使用的可能性更大些,就可以用弱引用傅联。
9.8 加載loading優(yōu)化
- 一般實(shí)際開發(fā)中會(huì)至少有兩種loading
- 第一種是從A頁(yè)面進(jìn)入B頁(yè)面時(shí)的加載loading先改,這個(gè)時(shí)候特點(diǎn)是顯示loading的時(shí)候,頁(yè)面是純白色的蒸走,加載完數(shù)據(jù)后才顯示內(nèi)容頁(yè)面仇奶。
- 第二種是在某個(gè)頁(yè)面操作某種邏輯,比如某些耗時(shí)操作比驻,這個(gè)時(shí)候是局部loading[一般用個(gè)幀動(dòng)畫或者補(bǔ)間動(dòng)畫]该溯,由于使用頻繁,因?yàn)榻ㄗh在銷毀彈窗時(shí)嫁艇,添加銷毀動(dòng)畫的操作朗伶。
- 自定義loading加載
9.9 對(duì)象池Pools優(yōu)化
- 對(duì)象池Pools優(yōu)化頻繁創(chuàng)建和銷毀對(duì)象
- 使用對(duì)象池,可以防止頻繁創(chuàng)建和銷毀對(duì)象而出現(xiàn)內(nèi)存抖動(dòng)
- 在某些時(shí)候步咪,我們需要頻繁使用一些臨時(shí)對(duì)象论皆,如果每次使用的時(shí)候都申請(qǐng)新的資源,很有可能會(huì)引發(fā)頻繁的 gc 而影響應(yīng)用的流暢性猾漫。這個(gè)時(shí)候如果對(duì)象有明確的生命周期点晴,那么就可以通過(guò)定義一個(gè)對(duì)象池來(lái)高效的完成復(fù)用對(duì)象。
- 具體參考案例悯周,可以看該項(xiàng)目:https://github.com/yangchong211/YCZoomImage
10.RecyclerView優(yōu)化
10.1 頁(yè)面為何卡頓
- RecyclerView滑動(dòng)卡頓的原因有哪些粒督?
- 第一種:嵌套布局滑動(dòng)沖突
- 導(dǎo)致嵌套滑動(dòng)難處理的關(guān)鍵原因在于當(dāng)子控件消費(fèi)了事件, 那么父控件就不會(huì)再有機(jī)會(huì)處理這個(gè)事件了, 所以一旦內(nèi)部的滑動(dòng)控件消費(fèi)了滑動(dòng)操作, 外部的滑動(dòng)控件就再也沒機(jī)會(huì)響應(yīng)這個(gè)滑動(dòng)操作了
- 第二種:嵌套布局層次太深,比如六七層等
- 測(cè)量禽翼,繪制布局可能會(huì)導(dǎo)致滑動(dòng)卡頓
- 第三種:比如用RecyclerView實(shí)現(xiàn)畫廊屠橄,加載比較大的圖片族跛,如果快速滑動(dòng),則可能會(huì)出現(xiàn)卡頓锐墙,主要是加載圖片需要時(shí)間
- 第四種:在onCreateViewHolder或者在onBindViewHolder中做了耗時(shí)的操作導(dǎo)致卡頓礁哄。按stackoverflow上面比較通俗的解釋:RecyclerView.Adapter里面的onCreateViewHolder()方法和onBindViewHolder()方法對(duì)時(shí)間都非常敏感。類似I/O讀寫溪北,Bitmap解碼一類的耗時(shí)操作桐绒,最好不要在它們里面進(jìn)行。
- 第一種:嵌套布局滑動(dòng)沖突
- 關(guān)于RecyclerView封裝庫(kù)
10.2 具體優(yōu)化方案
- 03.SparseArray替代HashMap
- 04.瀑布流圖片錯(cuò)亂問題解決
- 05.item點(diǎn)擊事件放在哪里優(yōu)化
- 06.ViewHolder優(yōu)化
- 07.連續(xù)上拉加載更多優(yōu)化
- 08.拖拽排序與滑動(dòng)刪除優(yōu)化
- 09.暫椭Γ或停止加載數(shù)據(jù)優(yōu)化
- 11.異常情況下保存狀態(tài)
- 12.多線程下插入數(shù)據(jù)優(yōu)化
- 14.recyclerView優(yōu)化處理
- 15.adapter優(yōu)化
- 具體看這篇博客:recyclerView優(yōu)化
關(guān)于其他內(nèi)容介紹
01.關(guān)于博客匯總鏈接
- 1.技術(shù)博客匯總
- 2.開源項(xiàng)目匯總
- 3.生活博客匯總
- 4.喜馬拉雅音頻匯總
- 5.其他匯總
02.關(guān)于我的博客
- 我的個(gè)人站點(diǎn):www.yczbj.org茉继,www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
- 簡(jiǎn)書:http://www.reibang.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
- 開源中國(guó):https://my.oschina.net/zbj1618/blog
- 泡在網(wǎng)上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles