StrictMode檢測Android中的違規代碼 (一)(內存泄露,IO操作,網絡操作)

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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章