1、最近看到一篇關於Android性能調優的文章,裏面提到了一個性能調優利器StrictMode,並且還是系統自帶,不需要第三方引入。(之前一直沒發現,慚愧)試着用它去檢測了一下之前的代碼,確實發現不少問題,特此記錄分享下。
2.關於使用比較簡單,建議在Applicaiton的oncreate方法中調用。
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
ps:第一行setThreadPolicy()這個方法顧名思義是開啓線程策略檢測(detectAll就是開啓下面的所有檢測)
- 自定義的耗時調用 使用detectCustomSlowCalls()開啓
- 磁盤讀取操作 使用detectDiskReads()開啓
- 磁盤寫入操作 使用detectDiskWrites()開啓
- 網絡操作 使用detectNetwork()開啓
第二行setVmPolicy()方法是開啓虛擬機策略檢測,就是各種泄露問題。
- Activity泄露 使用detectActivityLeaks()開啓
- 未關閉的Closable對象泄露 使用detectLeakedClosableObjects()開啓
- 泄露的Sqlite對象 使用detectLeakedSqlLiteObjects()開啓
- 檢測實例數量 使用setClassInstanceLimit()開啓
3.下面有這樣一行代碼,拿到外部sd卡的路徑,這行代碼放在主線程執行,正常情況下,是不會報錯的。但是實際上也屬於違規代碼。
@Override
public void onCreate() {
super.onCreate();
String path = Environment.getExternalStorageDirectory().getPath();
}
我們來看看報了什麼提示:
大概能知道什麼意思,就是說這句代碼屬於io操作,在主線程執行了,給出警告了。我相信很多人寫着行代碼都隨手在主線程寫了,當然這只是一個小例子,爲了說明一下io操作在主線程執行的隱患。
解決辦法:把這段代碼放在子線程執行就不會報警告了,至於結果回調處理,就不多說了。
4.一個關於activity實例導致內存泄露的問題,有個需求是token失效的時候,app請求網絡,後臺會返回失效碼,app這邊需要從A頁面跳轉到登陸頁面B,引導用戶重新登陸進入C頁面,所以,我在BaseActivity裏面把當前activity用一個list維護起來,當token跳轉到登陸頁面的時候,把list裏面的activity全部remove掉,確保當前list裏面只有登陸頁面的實例,(這裏面有個小問題就是,用戶不手動按返回鍵,而只是通過startactivity()跳轉到另一個頁面的時候,若沒有finish,上一個activity是不會走ondestory的,所以這個實例實際上應該是在的),所以,當A頁面跳轉到登陸頁面B的時候,需要手動調一下remove移除掉A,否則可能會導致A頁面沒有走ondestory方法,實例會一直存在。我用StrictMode打印日誌的時候發現,從C主頁跳轉到A DataActivity時,A的實例一直增加,仔細查了下代碼,發現,基類裏面添加了A進去,但是,ondestory的時候沒有調用remove當前activity的方法,導致,A實例一直增加。(即使用戶是手動按返回鍵退出A頁面,但因爲A已經被List引用了,所以,A的資源還是無法全部釋放,)
11-22 19:11:13.523 23937-23937/com.mzj.bd E/StrictMode: class com.mzj.bd.view.question.WebViewActivity; instances=2; limit=1
android.os.StrictMode$InstanceCountViolation: class com.mzj.bd.view.question.WebViewActivity; instances=2; limit=1
我們再來看看,Android studio自帶的內存分析工具,分析的結果。下圖右邊可以看到,點了6次,就有6個實例了,明顯的泄露了。
解決辦法也很簡單:
只需要在A的基類activity的ondestory方法裏面remove掉list裏面的實例就OK。
解決後無論跳轉多少次,都不會出現多個實例的情況了。
5.關於百度地圖mapview沒有及時銷燬導致的內存泄露問題。
(1)假如有一個加載百度地圖的SignLocationActivity,銷燬後必須伴隨mapview的onDestroy()方法,否則,會造成LocationActivity的泄露,一直回收不了。即使SignLocationActivity已經走了生命週期的ondestory方法。我們先看下StrictMode替我們打出的信息。
11-23 10:13:21.864 16150-16150 E/StrictMode: class com.location.SignLocationActivity; instances=2; limit=1
android.os.StrictMode$InstanceCountViolation: class com.location.SignLocationActivity; instances=2; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
很明顯,這是說SignLocationActivity的實例已經存在2個了,限制最多隻能存在一個。查找下代碼,發現SignLocationActivity的ondestory方法沒有及時調用mapview的ondestory方法。導致mapview引用到SignLocationActivity的資源無法銷燬。再看下此時的as打印的內存信息。(點了2次,並且手動gc一次,已經存在2個SignLocationActivity實例了,明顯的泄露了)
再看看這個,Shallow Size指的是該對象本身佔用內存的大小,Retained Size代表該對象被釋放後,垃圾回收器能回收的內存總和。如果改實例能正常回收,理論上Shallow Size和Retained Size大小基本一致,這裏的Retained Size 明顯大於Shallow Size,說明,有很多其他地方引用的資源,得不到釋放。原因也寫的很清楚,是SignLocationActivity的mcontext上下文,被mapview引用,mapview沒有銷燬掉,SignLocationActivity資源得不到釋放。
解決辦法:在SignLocationActivity的ondestory方法調用mapview的ondestory方法。
@Override
protected void onDestroy() {
super.onDestroy();
if(mBaiduMap!=null){
mBaiduMap.clear();
}
if(signMapView!=null){
signMapView.onDestroy();
}
}
6.還有一個關於百度地圖的內存泄露問題,在初始化LocationClient的時候,mLocationClient = new LocationClient(context);切記不要用當前activity的context,要用applicationContext,否則會出現下面的警告。
android.app.ServiceConnectionLeaked: Activity com.location.LocationActivity has leaked ServiceConnection com.baidu.location.b@fa723f1 that was originally bound here
at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1344)