摘要:錯誤使用單利在開發(fā)中單例經(jīng)常需要持有對象,如果持有的對象生命周期與單例生命周期更短時,或?qū)е聼o法被釋放回收,則有可能造成內(nèi)存泄漏。如果集合是類型的話,那內(nèi)存泄漏情況就會更為嚴(yán)重。
目錄介紹
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 動畫資源未釋放
2.1 錯誤使用單利
2.2 錯誤使用靜態(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代碼檢測
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ò)請求異常攔截優(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 啟動頁優(yōu)化
9.其他優(yōu)化
9.1 靜態(tài)變量優(yōu)化
9.2 注解替代枚舉
9.3 多渠道打包優(yōu)化
9.4 TrimMemory和LowMemory優(yōu)化
9.5 輪詢操作優(yōu)化
9.6 去除重復(fù)依賴庫優(yōu)化
9.7 四種引用優(yōu)化
9.8 加載loading優(yōu)化
9.9 對象池Pools優(yōu)化
10.RecyclerView優(yōu)化
10.1 頁面為何卡頓
10.2 具體優(yōu)化方案
好消息博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點(diǎn),Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護(hù)并且修正,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計(jì)N篇[近100萬字,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!
1.OOM和崩潰優(yōu)化 1.2 ANR優(yōu)化
ANR的產(chǎn)生需要滿足三個條件
主線程:只有應(yīng)用程序進(jìn)程的主線程響應(yīng)超時才會產(chǎn)生ANR;
超時時間:產(chǎn)生ANR的上下文不同,超時時間也會不同,但只要在這個時間上限內(nèi)沒有響應(yīng)就會ANR;
輸入事件/特定操作:輸入事件是指按鍵、觸屏等設(shè)備輸入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各個函數(shù),產(chǎn)生ANR的上下文不同,導(dǎo)致ANR的原因也會不同;
ANR優(yōu)化具體措施
將所有耗時操作,比如訪問網(wǎng)絡(luò),Socket通信,查詢大量SQL 語句,復(fù)雜邏輯計(jì)算等都放在子線程中去,然
后通過handler.sendMessage、runonUIThread、AsyncTask 等方式更新UI。無論如何都要確保用戶界面作的流暢 度。如果耗時操作需要讓用戶等待,那么可以在界面上顯示度條。 - 使用AsyncTask處理耗時IO操作。在一些同步的操作主線程有可能被鎖,需要等待其他線程釋放相應(yīng)鎖才能繼續(xù)執(zhí)行,這樣會有一定的ANR風(fēng)險,對于這種情況有時也可以用異步線程來執(zhí)行相應(yīng)的邏輯。另外,要避免死鎖的發(fā)生。 - 使用Handler處理工作線程結(jié)果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。 - Activity的onCreate和onResume回調(diào)中盡量避免耗時的代碼 - BroadcastReceiver中onReceive代碼也要盡量減少耗時,建議使用IntentService處理。 - 各個組件的生命周期函數(shù)都不應(yīng)該有太耗時的操作,即使對于后臺Service或者ContentProvider來講,應(yīng)用在后臺運(yùn)行時候其onCreate()時候不會有用戶輸入引起事件無響應(yīng)ANR,但其執(zhí)行時間過長也會引起Service的ANR和ContentProvider的ANR2.內(nèi)存泄漏優(yōu)化
內(nèi)存檢測第一種:代碼方式獲取內(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)存檢測第二種:leakcanary工具
LeakCanary的原理是監(jiān)控每個activity,在activity ondestory后,在后臺線程檢測引用,然后過一段時間進(jìn)行g(shù)c,gc后如果引用還在,那么dump出內(nèi)存堆棧,并解析進(jìn)行可視化顯示。
2.0 動畫資源未釋放
問題代碼
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(); } }
解決辦法
在屬性動畫中有一類無限循環(huán)動畫,如果在Activity中播放這類動畫并且在onDestroy中去停止動畫,那么這個動畫將會一直播放下去,這時候Activity會被View所持有,從而導(dǎo)致Activity無法被釋放。解決此類問題則是需要早Activity中onDestroy去去調(diào)用objectAnimator.cancel()來停止動畫。
@Override protected void onDestroy() { super.onDestroy(); mAnimator.cancel(); }2.1 錯誤使用單利
在開發(fā)中單例經(jīng)常需要持有Context對象,如果持有的Context對象生命周期與單例生命周期更短時,或?qū)е翪ontext無法被釋放回收,則有可能造成內(nèi)存泄漏。比如:在一個Activity中調(diào)用的,然后關(guān)閉該Activity則會出現(xiàn)內(nèi)存泄漏。
解決辦法:
要保證Context和AppLication的生命周期一樣,修改后代碼如下:
this.mContext = context.getApplicationContext();
1、如果此時傳入的是 Application 的 Context,因?yàn)?Application 的生命周期就是整個應(yīng)用的生命周期,所以這將沒有任何問題。
2、如果此時傳入的是 Activity 的 Context,當(dāng)這個 Context 所對應(yīng)的 Activity 退出時,由于該 Context 的引用被單例對象所持有,其生命周期等于整個應(yīng)用程序的生命周期,所以當(dāng)前 Activity 退出時它的內(nèi)存并不會被回收,這就造成泄漏了。
2.2 錯誤使用靜態(tài)變量使用靜態(tài)方法是十分方便的。但是創(chuàng)建的對象,建議不要全局化,全局化的變量必須加上static。全局化后的變量或者對象會導(dǎo)致內(nèi)存泄漏!
原因分析
這里內(nèi)部類AClass隱式的持有外部類Activity的引用,而在Activity的onCreate方法中調(diào)用了。這樣AClass就會在Activity創(chuàng)建的時候是有了他的引用,而AClass是靜態(tài)類型的不會被垃圾回收,Activity在執(zhí)行onDestory方法的時候由于被AClass持有了引用而無法被回收,所以這樣Activity就總是被AClass持有而無法回收造成內(nèi)存泄露。
2.3 handler內(nèi)存泄漏
造成內(nèi)存泄漏原因分析
通過內(nèi)部類的方式創(chuàng)建mHandler對象,此時mHandler會隱式地持有一個外部類對象引用這里就是MainActivity,當(dāng)執(zhí)行postDelayed方法時,該方法會將你的Handler裝入一個Message,并把這條Message推到MessageQueue中,MessageQueue是在一個Looper線程中不斷輪詢處理消息,那么當(dāng)這個Activity退出時消息隊(duì)列中還有未處理的消息或者正在處理消息,而消息隊(duì)列中的Message持有mHandler實(shí)例的引用,mHandler又持有Activity的引用,所以導(dǎo)致該Activity的內(nèi)存資源無法及時回收,引發(fā)內(nèi)存泄漏。
解決Handler內(nèi)存泄露主要2點(diǎn)
有延時消息,要在Activity銷毀的時候移除Messages監(jiān)聽
匿名內(nèi)部類導(dǎo)致的泄露改為匿名靜態(tài)內(nèi)部類,并且對上下文或者Activity使用弱引用。
2.4 線程造成內(nèi)存泄漏早時期的時候處理耗時操作多數(shù)都是采用Thread+Handler的方式,后來逐步被AsyncTask取代,直到現(xiàn)在采用RxJava的方式來處理異步。
造成內(nèi)存泄漏原因分析
在處理一個比較耗時的操作時,可能還沒處理結(jié)束MainActivity就執(zhí)行了退出操作,但是此時AsyncTask依然持有對MainActivity的引用就會導(dǎo)致MainActivity無法釋放回收引發(fā)內(nèi)存泄漏。
解決辦法
在使用AsyncTask時,在Activity銷毀時候也應(yīng)該取消相應(yīng)的任務(wù)AsyncTask.cancel()方法,避免任務(wù)在后臺執(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)存泄漏。有的時候我們可能會在啟動頻繁的Activity中,為了避免重復(fù)創(chuàng)建相同的數(shù)據(jù)資源,可能會出現(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會導(dǎo)致內(nèi)存泄漏 }
解決辦法
將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來封裝成一個單例,如果需要使用Context,請按照上面推薦的使用Application 的 Context。
分析問題
這樣就在Activity內(nèi)部創(chuàng)建了一個非靜態(tài)內(nèi)部類的單例,每次啟動Activity時都會使用該單例的數(shù)據(jù),這樣雖然避免了資源的重復(fù)創(chuàng)建,不過這種寫法卻會造成內(nèi)存泄漏,因?yàn)榉庆o態(tài)內(nèi)部類默認(rèn)會持有外部類的引用,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個靜態(tài)的實(shí)例,該實(shí)例的生命周期和應(yīng)用的一樣長,這就導(dǎo)致了該靜態(tài)實(shí)例一直會持有該Activity的引用,導(dǎo)致Activity的內(nèi)存資源不能正?;厥铡?/p>
2.6 未移除監(jiān)聽
問題代碼
//add監(jiān)聽,放到集合里面 tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { @Override public void onWindowFocusChanged(boolean b) { //監(jiān)聽view的加載,view加載出來的時候,計(jì)算他的寬高等。 } });
解決辦法
//計(jì)算完后,一定要移除這個監(jiān)聽 tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
注意事項(xiàng):
tv.setOnClickListener();//監(jiān)聽執(zhí)行完回收對象,不用考慮內(nèi)存泄漏 tv.getViewTreeObserver().addOnWindowFocusChangeListene,add監(jiān)聽,放到集合里面,需要考慮內(nèi)存泄漏2.7 持有activity引用 2.8 資源未關(guān)閉
在使用IO、File流或者Sqlite、Cursor等資源時要及時關(guān)閉。這些資源在進(jìn)行讀寫操作時通常都使用了緩沖,如果及時不關(guān)閉,這些緩沖對象就會一直被占用而得不到釋放,以致發(fā)生內(nèi)存泄露。因此我們在不需要使用它們的時候就及時關(guān)閉,以便緩沖能及時得到釋放,從而避免內(nèi)存泄露。
BroadcastReceiver,ContentObserver,F(xiàn)ileObserver,Cursor,Callback等在 Activity onDestroy 或者某類生命周期結(jié)束之后一定要 unregister 或者 close 掉,否則這個 Activity 類會被 system 強(qiáng)引用,不會被內(nèi)存回收。值得注意的是,關(guān)閉的語句必須在finally中進(jìn)行關(guān)閉,否則有可能因?yàn)楫惓N搓P(guān)閉資源,致使activity泄漏。
2.9 其他原因
靜態(tài)集合使用不當(dāng)導(dǎo)致的內(nèi)存泄漏
有時候我們需要把一些對象加入到集合容器(例如ArrayList)中,當(dāng)不再需要當(dāng)中某些對象時,如果不把該對象的引用從集合中清理掉,也會使得GC無法回收該對象。如果集合是static類型的話,那內(nèi)存泄漏情況就會更為嚴(yán)重。因此,當(dāng)不再需要某對象時,需要主動將之從集合中移除。
不需要用的監(jiān)聽未移除會發(fā)生內(nèi)存泄露
問題代碼
//add監(jiān)聽,放到集合里面 tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { @Override public void onWindowFocusChanged(boolean b) { //監(jiān)聽view的加載,view加載出來的時候,計(jì)算他的寬高等。 } });
解決辦法
//計(jì)算完后,一定要移除這個監(jiān)聽 tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
注意事項(xiàng):
tv.setOnClickListener();//監(jiān)聽執(zhí)行完回收對象,不用考慮內(nèi)存泄漏 tv.getViewTreeObserver().addOnWindowFocusChangeListene,add監(jiān)聽,放到集合里面,需要考慮內(nèi)存泄漏3.布局優(yōu)化 3.1 include優(yōu)化
重用布局文件
標(biāo)簽可以允許在一個布局當(dāng)中引入另一個布局,那么比如說我們程序的所有界面都有一個公共的部分,這個時候最好的做法就是將這個公共的部分提取到一個獨(dú)立的布局中,然后每個界面的布局文件當(dāng)中來引用這個公共的布局。
如果我們要在標(biāo)簽中覆寫layout屬性,必須要將layout_width和layout_height這兩個屬性也進(jìn)行覆寫,否則覆寫效果將不會生效。
標(biāo)簽是作為標(biāo)簽的一種輔助擴(kuò)展來使用的,它的主要作用是為了防止在引用布局文件時引用文件時產(chǎn)生多余的布局嵌套。布局嵌套越多,解析起來就越耗時,性能就越差。因此編寫布局文件時應(yīng)該讓嵌套的層數(shù)越少越好。
舉例:比如在LinearLayout里邊使用一個布局。里邊又有一個LinearLayout,那么其實(shí)就存在了多余的布局嵌套,使用merge可以解決這個問題。
3.2 ViewStub優(yōu)化
僅在需要時才加載布局[ViewStub]
某個布局當(dāng)中的元素不是一起顯示出來的,普通情況下只顯示部分常用的元素,而那些不常用的元素只有在用戶進(jìn)行特定操作時才會顯示出來。
舉例:填信息時不是需要全部填的,有一個添加更多字段的選項(xiàng),當(dāng)用戶需要添加其他信息的時候,才將另外的元素顯示到界面上。用VISIBLE性能表現(xiàn)一般,可以用ViewStub。
ViewStub也是View的一種,但是沒有大小,沒有繪制功能,也不參與布局,資源消耗非常低,可以認(rèn)為完全不影響性能。
ViewStub所加載的布局是不可以使用標(biāo)簽的,因此這有可能導(dǎo)致加載出來出來的布局存在著多余的嵌套結(jié)構(gòu)。
自定義全局的狀態(tài)管理器【充分使用ViewStub】
針對多狀態(tài),有數(shù)據(jù),空數(shù)據(jù),加載失敗,加載異常,網(wǎng)絡(luò)異常等。針對空數(shù)據(jù),加載失敗,異常使用viewStub布局,一鍵設(shè)置自定義布局,也是優(yōu)化的一種。
項(xiàng)目地址:
3.3 merge優(yōu)化
視圖層級
這個標(biāo)簽在UI的結(jié)構(gòu)優(yōu)化中起著非常重要的作用,它可以刪減多余的層級,優(yōu)化UI。但是就有一點(diǎn)不好,無法預(yù)覽布局效果!
3.4 其他建議
減少太多重疊的背景(overdraw)
這個問題其實(shí)最容易解決,建議就是檢查你在布局和代碼中設(shè)置的背景,有些背景是隱藏在底下的,它永遠(yuǎn)不可能顯示出來,這種沒必要的背景一定要移除,因?yàn)樗芸赡軙?yán)重影響到app的性能。如果采用的是selector的背景,將normal狀態(tài)的color設(shè)置為”@android:color/transparent”,也同樣可以解決問題。
避免復(fù)雜的Layout層級
這里的建議比較多一些,首先推薦使用Android提供的布局工具Hierarchy Viewer來檢查和優(yōu)化布局。第一個建議是:如果嵌套的線性布局加深了布局層次,可以使用相對布局來取代。第二個建議是:用標(biāo)簽來合并布局。第三個建議是:用標(biāo)簽來重用布局,抽取通用的布局可以讓布局的邏輯更清晰明了。記住,這些建議的最終目的都是使得你的Layout在Hierarchy Viewer里變得寬而淺,而不是窄而深。
總結(jié):可以考慮多使用merge和include,ViewStub。盡量使布局淺平,根布局盡量少使用RelactivityLayout,因?yàn)镽elactivityLayout每次需要測量2次。
4.代碼優(yōu)化都是一些微優(yōu)化,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段。
4.1 建議使用lint檢查去除無效代碼
lint去除無效資源和代碼
如何檢測哪些圖片未被使用
點(diǎn)擊菜單欄 Analyze -> Run Inspection by Name -> unused resources -> Moudule ‘a(chǎn)pp’ -> OK,這樣會搜出來哪些未被使用到未使用到xml和圖片,如下:
如何檢測哪些無效代碼
使用Android Studio的Lint,步驟:點(diǎn)擊菜單欄 Analyze -> Run Inspection by Name -> unused declaration -> Moudule ‘a(chǎn)pp’ -> OK
4.2 代碼規(guī)范優(yōu)化
避免創(chuàng)建不必要的對象 不必要的對象應(yīng)該避免創(chuàng)建:
如果有需要拼接的字符串,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來進(jìn)行拼接,而不是加號連接符,因?yàn)槭褂眉犹栠B接符會創(chuàng)建多余的對象,拼接的字符串越長,加號連接符的性能越低。
當(dāng)一個方法的返回值是String的時候,通常需要去判斷一下這個String的作用是什么,如果明確知道調(diào)用方會將返回的String再進(jìn)行拼接操作的話,可以考慮返回一個StringBuffer對象來代替,因?yàn)檫@樣可以將一個對象的引用進(jìn)行返回,而返回String的話就是創(chuàng)建了一個短生命周期的臨時對象。
盡可能地少創(chuàng)建臨時對象,越少的對象意味著越少的GC操作。
nDraw方法里面不要執(zhí)行對象的創(chuàng)建
靜態(tài)優(yōu)于抽象
如果你并不需要訪問一個對系那個中的某些字段,只是想調(diào)用它的某些方法來去完成一項(xiàng)通用的功能,那么可以將這個方法設(shè)置成靜態(tài)方法,調(diào)用速度提升15%-20%,同時也不用為了調(diào)用這個方法去專門創(chuàng)建對象了,也不用擔(dān)心調(diào)用這個方法后是否會改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)。
對常量使用static final修飾符
static int intVal = 42; static String strVal = "Hello, world!";
編譯器會為上面的代碼生成一個初始方法,稱為方法,該方法會在定義類第一次被使用的時候調(diào)用。這個方法會將42的值賦值到intVal當(dāng)中,從字符串常量表中提取一個引用賦值到strVal上。當(dāng)賦值完成后,我們就可以通過字段搜尋的方式去訪問具體的值了。
final進(jìn)行優(yōu)化:
static final int intVal = 42; static final String strVal = "Hello, world!";
這樣,定義類就不需要方法了,因?yàn)樗械某A慷紩赿ex文件的初始化器當(dāng)中進(jìn)行初始化。當(dāng)我們調(diào)用intVal時可以直接指向42的值,而調(diào)用strVal會用一種相對輕量級的字符串常量方式,而不是字段搜尋的方式。
這種優(yōu)化方式只對基本數(shù)據(jù)類型以及String類型的常量有效,對于其他數(shù)據(jù)類型的常量是無效的。
在沒有特殊原因的情況下,盡量使用基本數(shù)據(jù)類型來代替封裝數(shù)據(jù)類型,int比Integer要更加有效,其它數(shù)據(jù)類型也是一樣。
基本數(shù)據(jù)類型的數(shù)組也要優(yōu)于對象數(shù)據(jù)類型的數(shù)組。另外兩個平行的數(shù)組要比一個封裝好的對象數(shù)組更加高效,舉個例子,F(xiàn)oo[]和Bar[]這樣的數(shù)組,使用起來要比Custom(Foo,Bar)[]這樣的一個數(shù)組高效的多。
4.3 View異常優(yōu)化
view自定義控件異常銷毀保存狀態(tài)
經(jīng)常容易被人忽略,但是為了追求高質(zhì)量代碼,這個也有必要加上。舉個例子!
@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); }4.4 去除淡黃色警告優(yōu)化
淡黃色警告雖然不會造成崩潰,但是作為程序員還是要盡量去除淡黃色警告,規(guī)范代碼
4.5 合理使用集合
使用優(yōu)化過的數(shù)據(jù)集合
Android提供了一系列優(yōu)化過后的數(shù)據(jù)集合工具類,如SparseArray、SparseBooleanArray、LongSparseArray,使用這些API可以讓我們的程序更加高效。HashMap工具類會相對比較低效,因?yàn)樗枰獮槊恳粋€鍵值對都提供一個對象入口,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對象數(shù)據(jù)類型的時間。
4.6 Activity不可見優(yōu)化
當(dāng)Activity界面不可見時釋放內(nèi)存
當(dāng)用戶打開了另外一個程序,我們的程序界面已經(jīng)不可見的時候,我們應(yīng)當(dāng)將所有和界面相關(guān)的資源進(jìn)行釋放。重寫Activity的onTrimMemory()方法,然后在這個方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個級別,一旦觸發(fā)說明用戶離開了程序,此時就可以進(jìn)行資源釋放操作了。
當(dāng)時看到這個覺得很新奇的,但是具體還是沒有用到,要是那個大神有具體操作方案,可以分享一下。
4.7 節(jié)制的使用Service
節(jié)制的使用Service
如果應(yīng)用程序需要使用Service來執(zhí)行后臺任務(wù)的話,只有當(dāng)任務(wù)正在執(zhí)行的時候才應(yīng)該讓Service運(yùn)行起來。當(dāng)啟動一個Service時,系統(tǒng)會傾向于將這個Service所依賴的進(jìn)程進(jìn)行保留,系統(tǒng)可以在LRUcache當(dāng)中緩存的進(jìn)程數(shù)量也會減少,導(dǎo)致切換程序的時候耗費(fèi)更多性能。我們可以使用IntentService,當(dāng)后臺任務(wù)執(zhí)行結(jié)束后會自動停止,避免了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)條件下加載縮略圖【也稱與加載圖】。
簡單來說根據(jù)用戶的當(dāng)前的網(wǎng)絡(luò)質(zhì)量來判斷下載什么質(zhì)量的圖片(電商用的比較多)。豆瓣開源接口可以參考一下!
5.2 獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化移動端獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化的幾個點(diǎn)
連接復(fù)用:節(jié)省連接建立時間,如開啟 keep-alive。
對于Android來說默認(rèn)情況下HttpURLConnection和HttpClient都開啟了keep-alive。只是2.2之前HttpURLConnection存在影響連接池的Bug,具體可見:Android HttpURLConnection及HttpClient選擇
請求合并:即將多個請求合并為一個進(jìn)行請求,比較常見的就是網(wǎng)頁中的CSS Image Sprites。如果某個頁面內(nèi)請求過多,也可以考慮做一定的請求合并。
減少請求數(shù)據(jù)的大小:對于post請求,body可以做gzip壓縮的,header也可以做數(shù)據(jù)壓縮(不過只支持http
返回數(shù)據(jù)的body也可以做gzip壓縮,body數(shù)據(jù)體積可以縮小到原來的30%左右。(也可以考慮壓縮返回的json數(shù)據(jù)的key數(shù)據(jù)的體積,尤其是針對返回數(shù)據(jù)格式變化不大的情況,支付寶聊天返回的數(shù)據(jù)用到了)
5.3 網(wǎng)絡(luò)請求異常攔截優(yōu)化
在獲取數(shù)據(jù)的流程中,訪問接口和解析數(shù)據(jù)時都有可能會出錯,我們可以通過攔截器在這兩層攔截錯誤。
1.在訪問接口時,我們不用設(shè)置攔截器,因?yàn)橐坏┏霈F(xiàn)錯誤,Retrofit會自動拋出異常。比如,常見請求異常404,500,503等等。
2.在解析數(shù)據(jù)時,我們設(shè)置一個攔截器,判斷Result里面的code是否為成功,如果不成功,則要根據(jù)與服務(wù)器約定好的錯誤碼來拋出對應(yīng)的異常。比如,token失效,禁用同賬號登陸多臺設(shè)備,缺少參數(shù),參數(shù)傳遞異常等等。
3.除此以外,為了我們要盡量避免在View層對錯誤進(jìn)行判斷,處理,我們必須還要設(shè)置一個攔截器,攔截onError事件,然后使用ExceptionUtils,讓其根據(jù)錯誤類型來分別處理。
具體可以直接看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)存抖動現(xiàn)象,而發(fā)生了內(nèi)存抖動,對于移動端來說,最大的影響就是造成界面卡頓
線程的創(chuàng)建和銷毀都需要時間,當(dāng)有大量的線程創(chuàng)建和銷毀時,那么這些時間的消耗則比較明顯,將導(dǎo)致性能上的缺失
為什么要用線程池
重用線程池中的線程,避免頻繁地創(chuàng)建和銷毀線程帶來的性能消耗;有效控制線程的最大并發(fā)數(shù)量,防止線程過大導(dǎo)致?lián)屨假Y源造成系統(tǒng)阻塞;可以對線程進(jìn)行一定地管理。
使用線程池管理的經(jīng)典例子
RxJava,RxAndroid,底層對線程池的封裝管理特別值得參考
關(guān)于線程池,線程,多線程的具體內(nèi)容
參考:輕量級線程池封裝庫,支持異步回調(diào),可以檢測線程執(zhí)行的狀態(tài)
該項(xiàng)目中哪里用到頻繁new Thread
保存圖片[注意,尤其是大圖和多圖場景下注意耗時太久];某些頁面從數(shù)據(jù)庫查詢數(shù)據(jù);設(shè)置中心清除圖片,視頻,下載文件,日志,系統(tǒng)緩存等緩存內(nèi)容
使用線程池管理庫好處,比如保存圖片,耗時操作放到子線程中,處理過程中,可以檢測到執(zhí)行開始,異常,成功,失敗等多種狀態(tài)。
7.圖片優(yōu)化 7.1 bitmap優(yōu)化
加載圖片所占的內(nèi)存大小計(jì)算方式
加載網(wǎng)絡(luò)圖片:bitmap內(nèi)存大小 = 圖片長度 x 圖片寬度 x 單位像素占用的字節(jié)數(shù)【看到網(wǎng)上很多都是這樣寫的,但是不全面】
加載本地圖片:bitmap內(nèi)存大小 = width height nTargetDensity/inDensity 一個像素所占的內(nèi)存。注意不要忽略了一個影響項(xiàng):Density
第一種加載圖片優(yōu)化處理:壓縮圖片
質(zhì)量壓縮方法:在保持像素的前提下改變圖片的位深及透明度等,來達(dá)到壓縮圖片的目的,這樣適合去傳遞二進(jìn)制的圖片數(shù)據(jù),比如分享圖片,要傳入二進(jìn)制數(shù)據(jù)過去,限制500kb之內(nèi)。
采樣率壓縮方法:設(shè)置inSampleSize的值(int類型)后,假如設(shè)為n,則寬和高都為原來的1/n,寬高都減少,內(nèi)存降低。
縮放法壓縮:Android中使用Matrix對圖像進(jìn)行縮放、旋轉(zhuǎn)、平移、斜切等變換的。功能十分強(qiáng)大!
第二種加載圖片優(yōu)化:不壓縮加載高清圖片如何做?
使用BitmapRegionDecoder,主要用于顯示圖片的某一塊矩形區(qū)域,如果你需要顯示某個圖片的指定區(qū)域,那么這個類非常合適。
7.2 glide加載優(yōu)化
在畫廊中加載大圖
假如你滑動特別快,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(); } } });8.加載優(yōu)化 8.1 懶加載優(yōu)化
該優(yōu)化在新聞類app中十分常見
ViewPager+Fragment的搭配在日常開發(fā)中也比較常見,可用于切換展示不同類別的頁面。
懶加載,其實(shí)也就是延遲加載,就是等到該頁面的UI展示給用戶時,再加載該頁面的數(shù)據(jù)(從網(wǎng)絡(luò)、數(shù)據(jù)庫等),而不是依靠ViewPager預(yù)加載機(jī)制提前加載兩三個,甚至更多頁面的數(shù)據(jù)。這樣可以提高所屬Activity的初始化速度,也可以為用戶節(jié)省流量.而這種懶加載的方式也已經(jīng)/正在被諸多APP所采用。
具體看這篇文章
https://www.jianshu.com/p/cf1...
8.2 啟動頁優(yōu)化
啟動時間分析
系統(tǒng)創(chuàng)建進(jìn)程的時間和應(yīng)用進(jìn)程啟動的時間,前者是由系統(tǒng)自行完成的,一般都會很快,我們也干預(yù)不了,我覺得能做的就是去優(yōu)化應(yīng)用進(jìn)程啟動,具體說來就是從發(fā)Application的onCreate()執(zhí)行開始到MainActivity的onCreate()執(zhí)行結(jié)束這一段時間。
啟動時間優(yōu)化
Application的onCreate()方法
MainActivity的onCreate()方法
優(yōu)化的手段也無非三種,如下所示:
延遲初始化
后臺任務(wù)
啟動界面預(yù)加載
啟動頁白屏優(yōu)化
為什么存在這個問題?
當(dāng)系統(tǒng)啟動一個APP時,zygote進(jìn)程會首先創(chuàng)建一個新的進(jìn)程去運(yùn)行這個APP,但是進(jìn)程的創(chuàng)建是需要時間的,在創(chuàng)建完成之前,界面是呈現(xiàn)假死狀態(tài),于是系統(tǒng)根據(jù)你的manifest文件設(shè)置的主題顏色的不同來展示一個白屏或者黑屏。而這個黑(白)屏正式的稱呼應(yīng)該是Preview Window,即預(yù)覽窗口。
實(shí)際上就是是activity默認(rèn)的主題中的android:windowBackground為白色或者黑色導(dǎo)致的。
總結(jié)來說啟動順序就是:app啟動——Preview Window(也稱為預(yù)覽窗口)——啟動頁
解決辦法
常見有三種,這里解決辦法是給當(dāng)前啟動頁添加一個有背景的style樣式,然后SplashActivity引用當(dāng)前theme主題,注意在該頁面將window的背景圖設(shè)置為空!
更多關(guān)于啟動頁為什么白屏閃屏,以及不同解決辦法,可以看我這篇博客:App啟動頁面優(yōu)化
啟動時間優(yōu)化
IntentService子線程分擔(dān)部分初始化工作
現(xiàn)在application初始化內(nèi)容有:阿里云推送初始化,騰訊bugly初始化,im初始化,神策初始化,內(nèi)存泄漏工具初始化,頭條適配方案初始化,阿里云熱修復(fù)……等等。將部分邏輯放到IntentService中處理,可以縮短很多時間。
開啟IntentSerVice線程,將部分邏輯和耗時的初始化操作放到這里處理,可以減少application初始化時間
關(guān)于IntentService使用和源碼分析,性能分析等可以參考博客:IntentService源碼分析
9.其他優(yōu)化 9.1 靜態(tài)變量優(yōu)化
盡量不使用靜態(tài)變量保存核心數(shù)據(jù)。這是為什么呢?
這是因?yàn)閍ndroid的進(jìn)程并不是安全的,包括application對象以及靜態(tài)變量在內(nèi)的進(jìn)程級別變量并不會一直呆著內(nèi)存里面,因?yàn)樗苡袝籯ill掉。
當(dāng)被kill掉之后,實(shí)際上app不會重新開始啟動。Android系統(tǒng)會創(chuàng)建一個新的Application對象,然后啟動上次用戶離開時的activity以造成這個app從來沒有被kill掉的假象。而這時候靜態(tài)變量等數(shù)據(jù)由于進(jìn)程已經(jīng)被殺死而被初始化,所以就有了不推薦在靜態(tài)變量(包括Application中保存全局?jǐn)?shù)據(jù)靜態(tài)數(shù)據(jù))的觀點(diǎn)。
9.2 注解替代枚舉
使用注解限定傳入類型
比如,尤其是寫第三方開源庫,對于有些暴露給開發(fā)者的方法,需要限定傳入類型是有必要的。舉個例子:
剛開始的代碼
/** * 設(shè)置播放器類型,必須設(shè)置 * 注意:感謝某人建議,這里限定了傳入值類型 * 輸入值:111 或者 222
*/ public void setPlayerType(int playerType) { mPlayerType = playerType; } ``` - 優(yōu)化后的代碼,有效避免第一種方式開發(fā)者傳入值錯誤 ``` /** * 設(shè)置播放器類型,必須設(shè)置 * 注意:感謝某人建議,這里限定了傳入值類型 * 輸入值:ConstantKeys.IjkPlayerType.TYPE_IJK 或者 ConstantKeys.IjkPlayerType.TYPE_NATIVE * @param playerType IjkPlayer or MediaPlayer. */ public void setPlayerType(@ConstantKeys.PlayerType int playerType) { mPlayerType = playerType; } /** * 通過注解限定類型 * 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)化
還在手動打包嗎?嘗試一下python自動化打包吧……
瓦力多渠道打包的Python腳本測試工具,通過該自動化腳本,自需要run一下或者命令行運(yùn)行腳本即可實(shí)現(xiàn)美團(tuán)瓦力多渠道打包,打包速度很快。配置信息十分簡單,代碼中已經(jīng)注釋十分詳細(xì)??梢宰远x輸出文件路徑,可以修改多渠道配置信息,簡單實(shí)用。 項(xiàng)目地址:https://github.com/yangchong2...
9.4 TrimMemory和LowMemory優(yōu)化
可以優(yōu)化什么?
在 onTrimMemory() 回調(diào)中,應(yīng)該在一些狀態(tài)下清理掉不重要的內(nèi)存資源。對于這些緩存,只要是讀進(jìn)內(nèi)存內(nèi)的都算,例如最常見的圖片緩存、文件緩存等。拿圖片緩存來說,市場上,常規(guī)的圖片加載庫,一般而言都是三級緩存,所以在內(nèi)存吃緊的時候,我們就應(yīng)該優(yōu)先清理掉這部分圖片緩存,畢竟圖片是吃內(nèi)存大戶,而且再次回來的時候,雖然內(nèi)存中的資源被回收掉了,依然可以從磁盤或者網(wǎng)絡(luò)上恢復(fù)它。
大概的思路如下所示
在lowMemory的時候,調(diào)用Glide.cleanMemory()清理掉所有的內(nèi)存緩存。
在App被置換到后臺的時候,調(diào)用Glide.cleanMemory()清理掉所有的內(nèi)存緩存。
在其它情況的onTrimMemory()回調(diào)中,直接調(diào)用Glide.trimMemory()方法來交給Glide處理內(nèi)存情況。
9.5 輪詢操作優(yōu)化
什么叫輪訓(xùn)請求?
簡單理解就是App端每隔一定的時間重復(fù)請求的操作就叫做輪訓(xùn)請求,比如:App端每隔一段時間上報一次定位信息,App端每隔一段時間拉去一次用戶狀態(tài)等,這些應(yīng)該都是輪訓(xùn)請求。比如,電商類項(xiàng)目,某個抽獎活動頁面,隔1分鐘調(diào)用一次接口,彈出一些獲獎人信息,你應(yīng)該某個階段看過這類輪詢操作!
具體優(yōu)化操作
長連接并不是穩(wěn)定的可靠的,而執(zhí)行輪訓(xùn)操作的時候一般都是要穩(wěn)定的網(wǎng)絡(luò)請求,而且輪訓(xùn)操作一般都是有生命周期的,即在一定的生命周期內(nèi)執(zhí)行輪訓(xùn)操作,而長連接一般都是整個進(jìn)程生命周期的,所以從這方面講也不太適合。
建議在service中做輪詢操作,輪詢請求接口,具體做法和注意要點(diǎn),可以直接看該項(xiàng)目代碼??碼pp包下的LoopRequestService類即可。
大概思路:當(dāng)用戶打開這個頁面的時候初始化TimerTask對象,每個一分鐘請求一次服務(wù)器拉取訂單信息并更新UI,當(dāng)用戶離開頁面的時候清除TimerTask對象,即取消輪訓(xùn)請求操作。
9.6 去除重復(fù)依賴庫優(yōu)化
我相信你看到了這里會有疑問,網(wǎng)上有許多博客作了這方面說明。但是我在這里想說,如何查找自己項(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
然后修改gradle配置代碼
api (rootProject.ext.dependencies["zxing"]){ exclude module: "support-v4" exclude module: "appcompat-v7" }9.7 四種引用優(yōu)化
軟引用使用場景
正常是用來處理大圖片這種占用內(nèi)存大的情況
代碼如下所示
Bitmap bitmap = bitmaps.get(position); //正常是用來處理圖片這種占用內(nèi)存大的情況 bitmapSoftReference = new SoftReference<>(bitmap); if(bitmapSoftReference.get() != null) { viewHolder.imageView.setImageBitmap(bitmapSoftReference.get()); } //其實(shí)看glide底層源碼可知,也做了相關(guān)軟引用的操作
這樣使用軟引用好處
通過軟引用的get()方法,取得bitmap對象實(shí)例的強(qiáng)引用,發(fā)現(xiàn)對象被未回收。在GC在內(nèi)存充足的情況下,不會回收軟引用對象。此時view的背景顯示
實(shí)際情況中,我們會獲取很多圖片.然后可能給很多個view展示, 這種情況下很容易內(nèi)存吃緊導(dǎo)致oom,內(nèi)存吃緊,系統(tǒng)開始會GC。這次GC后,bitmapSoftReference.get()不再返回bitmap對象,而是返回null,這時屏幕上背景圖不顯示,說明在系統(tǒng)內(nèi)存緊張的情況下,軟引用被回收。
使用軟引用以后,在OutOfMemory異常發(fā)生之前,這些緩存的圖片資源的內(nèi)存空間可以被釋放掉的,從而避免內(nèi)存達(dá)到上限,避免Crash發(fā)生。
弱引用使用場景
弱引用–>隨時可能會被垃圾回收器回收,不一定要等到虛擬機(jī)內(nèi)存不足時才強(qiáng)制回收。
對于使用頻次少的對象,希望盡快回收,使用弱引用可以保證內(nèi)存被虛擬機(jī)回收。比如handler,如果希望使用完后盡快回收,看下面代碼
private MyHandler handler = new MyHandler(this); private static class MyHandler extends Handler{ WeakReferenceweakReference; MyHandler(FirstActivity activity) { weakReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ } } }
到底什么時候使用軟引用,什么時候使用弱引用呢?
個人認(rèn)為,如果只是想避免OutOfMemory異常的發(fā)生,則可以使用軟引用。如果對于應(yīng)用的性能更在意,想盡快回收一些占用內(nèi)存比較大的對象,則可以使用弱引用。
還有就是可以根據(jù)對象是否經(jīng)常使用來判斷。如果該對象可能會經(jīng)常使用的,就盡量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱引用。
9.8 加載loading優(yōu)化
一般實(shí)際開發(fā)中會至少有兩種loading
第一種是從A頁面進(jìn)入B頁面時的加載loading,這個時候特點(diǎn)是顯示loading的時候,頁面是純白色的,加載完數(shù)據(jù)后才顯示內(nèi)容頁面。
第二種是在某個頁面操作某種邏輯,比如某些耗時操作,這個時候是局部loading[一般用個幀動畫或者補(bǔ)間動畫],由于使用頻繁,因?yàn)榻ㄗh在銷毀彈窗時,添加銷毀動畫的操作。
自定義loading加載
https://github.com/yangchong2...
9.9 對象池Pools優(yōu)化對象池Pools優(yōu)化頻繁創(chuàng)建和銷毀對象
使用對象池,可以防止頻繁創(chuàng)建和銷毀對象而出現(xiàn)內(nèi)存抖動
在某些時候,我們需要頻繁使用一些臨時對象,如果每次使用的時候都申請新的資源,很有可能會引發(fā)頻繁的 gc 而影響應(yīng)用的流暢性。這個時候如果對象有明確的生命周期,那么就可以通過定義一個對象池來高效的完成復(fù)用對象。
具體參考案例,可以看該項(xiàng)目:https://github.com/yangchong2...
10.RecyclerView優(yōu)化 10.1 頁面為何卡頓
RecyclerView滑動卡頓的原因有哪些?
第一種:嵌套布局滑動沖突
導(dǎo)致嵌套滑動難處理的關(guān)鍵原因在于當(dāng)子控件消費(fèi)了事件, 那么父控件就不會再有機(jī)會處理這個事件了, 所以一旦內(nèi)部的滑動控件消費(fèi)了滑動操作, 外部的滑動控件就再也沒機(jī)會響應(yīng)這個滑動操作了
第二種:嵌套布局層次太深,比如六七層等
測量,繪制布局可能會導(dǎo)致滑動卡頓
第三種:比如用RecyclerView實(shí)現(xiàn)畫廊,加載比較大的圖片,如果快速滑動,則可能會出現(xiàn)卡頓,主要是加載圖片需要時間
第四種:在onCreateViewHolder或者在onBindViewHolder中做了耗時的操作導(dǎo)致卡頓。按stackoverflow上面比較通俗的解釋:RecyclerView.Adapter里面的onCreateViewHolder()方法和onBindViewHolder()方法對時間都非常敏感。類似I/O讀寫,Bitmap解碼一類的耗時操作,最好不要在它們里面進(jìn)行。
關(guān)于RecyclerView封裝庫
https://github.com/yangchong2...
10.2 具體優(yōu)化方案03.SparseArray替代HashMap
04.瀑布流圖片錯亂問題解決
05.item點(diǎn)擊事件放在哪里優(yōu)化
06.ViewHolder優(yōu)化
07.連續(xù)上拉加載更多優(yōu)化
08.拖拽排序與滑動刪除優(yōu)化
09.暫?;蛲V辜虞d數(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)于我的博客我的個人站點(diǎn):www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
項(xiàng)目開源地址:https://github.com/yangchong2...文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/77792.html
摘要:然而,中依然有可能發(fā)生內(nèi)存泄漏。所以你的安卓快速定位解決內(nèi)存泄漏掘金昨天是個好日子,程序員的節(jié)日,在這里給所有的程序員送上一份遲到的祝福。應(yīng)用內(nèi)存泄漏的定位分析與解決策略掘金,大家好,我是。 Android 性能優(yōu)化之巧用軟引用與弱引用優(yōu)化內(nèi)存使用 - Android - 掘金前言: 從事Android開發(fā)的同學(xué)都知道移動設(shè)備的內(nèi)存使用是非常敏感的話題,今天我們來看下如何使用軟引用與弱...
摘要:導(dǎo)語智能手機(jī)發(fā)展到今天已經(jīng)有十幾個年頭,手機(jī)的軟硬件都已經(jīng)發(fā)生了翻天覆地的變化,特別是陣營,從一開始的一兩百到今天動輒,內(nèi)存。恰好最近做了內(nèi)存優(yōu)化相關(guān)的工作,這里也對內(nèi)存優(yōu)化相關(guān)的知識做下總結(jié)。 導(dǎo)語 智能手機(jī)發(fā)展到今天已經(jīng)有十幾個年頭,手機(jī)的軟硬件都已經(jīng)發(fā)生了翻天覆地的變化,特別是Android陣營,從一開始的一兩百M(fèi)到今天動輒4G,6G內(nèi)存。然而大部分的開發(fā)者觀看下自己的異常上報系...
閱讀 3272·2021-11-10 11:35
閱讀 1472·2019-08-30 13:20
閱讀 1172·2019-08-29 16:18
閱讀 2204·2019-08-26 13:54
閱讀 2213·2019-08-26 13:50
閱讀 1007·2019-08-26 13:39
閱讀 2552·2019-08-26 12:08
閱讀 2005·2019-08-26 10:37