Android性能優化第(七)篇---StrickMode嚴苛模式檢測耗時與內存問題

StrictMode意思爲嚴格模式,是用來檢測程序中違例情況的開發者工具。使用一般是場景是檢測主線程中本地磁盤和網絡讀寫等耗時的操作。注意這個StrictMode是在Anroid2.3以後引入的。嚴格模式主要檢測兩大問題,一個是線程策略,即TreadPolicy,另一個是VM策略,即VmPolicy。

線程策略(ThreadPolicy)檢測的內容有

  • 1、自定義的耗時調用 使用detectCustomSlowCalls()開啓
  • 2、磁盤讀取操作 使用detectDiskReads()開啓
  • 3、磁盤寫入操作 使用detectDiskWrites()開啓
  • 4、網絡操作 使用detectNetwork()開啓

虛擬機策略(VmPolicy)檢測的內容有

  • 1、Activity泄露 使用detectActivityLeaks()開啓
  • 2、未關閉的Closable對象泄露 使用detectLeakedClosableObjects()開啓
  • 3、泄露的Sqlite對象 使用detectLeakedSqlLiteObjects()開啓
  • 4、檢測實例數量 使用setClassInstanceLimit()開啓

可以看到線程策略主要與異步處理相關,虛擬機策略主要與內存管理相關。setThreadPolicy()將對當前線程應用該策略。如果不指定檢測函數,也可以用detectAll()來替代。penaltyLog()表示將警告輸出到LogCat,你也可以使用其他或增加新的懲罰(penalty)函數,例如使用penaltyDeath()的話,一旦StrictMode消息被寫到LogCat後應用就會崩潰。另外虛擬機策略(VmPolicy)不能通過一個對話框提供警告。

在線程策略(ThreadPolicy)檢測的時候,有幾個penalty系列方法。

  • 1、penaltyDeath(),當觸發違規條件時,直接Crash掉當前應用程序。
  • 2、penaltyDeathOnNetwork(),當觸發網絡違規時,Crash掉當前應用程序。
  • 3、penaltyDialog(),觸發違規時,顯示對違規信息對話框。
  • 4、penaltyFlashScreen(),會造成屏幕閃爍,不過一般的設備可能沒有這個功能。

如何使用嚴格模式

嚴格模式的開啓可以放在Application或者Activity以及其他組件的onCreate方法。爲了更好地分析應用中的問題,建議放在Application的onCreate方法中。設置一次就夠了。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()//開啓所有的detectXX系列方法
                .penaltyDialog()//彈出違規提示框
                .penaltyLog()//在Logcat中打印違規日誌
                .build());
        requestDataFromNet();
    }

    /**
     * 請求數據
     */
    private void requestDataFromNet() {
        URL url;
        try {
            url = new URL("http://www.baidu.com");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String lines ;
            StringBuilder sb = new StringBuilder();
            while ((lines = reader.readLine()) != null) {
                sb.append(lines);
            }

            Log.d("response",sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

比如上面代碼運行之後,就會有下面的彈窗出來警告。

看一下logcat的日誌,是在主線程中做了訪問網絡的操作。

12-19 17:28:54.226 2729-2729/? D/StrictMode: StrictMode policy violation; ~duration=44 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4
            at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1123)
            at java.net.InetAddress.lookupHostByName(InetAddress.java:385)
            at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
            at java.net.InetAddress.getAllByName(InetAddress.java:214)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:70)
            at libcore.net.http.HttpConnection.<init>(HttpConnection.java:50)
            at libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:340)
            at libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:87)
            at libcore.net.http.HttpConnection.connect(HttpConnection.java:128)
            at libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:316)
            at libcore.net.http.HttpEngine.connect(HttpEngine.java:311)
            at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
            at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
            at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:81)
            at zhangwan.wj.com.myshare.MainActivity.requestDataFromNet(MainActivity.java:35)
            at zhangwan.wj.com.myshare.MainActivity.onCreate(MainActivity.java:27)
            at android.app.Activity.performCreate(Activity.java:5104)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1092)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2254)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5069)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
            at dalvik.system.NativeStart.main(Native Method)

上面分析了是異步處理相關的例子,現在分析一個內存管相關的例子,比如檢測內存泄露檢測,用StrickMode怎麼搞?

 public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectActivityLeaks()//檢測Activity泄露
                .penaltyLog()//在Logcat中打印違規日誌
                .build());

        UserManger instance = UserManger.getInstance(this);
    }

}

比如上面的代碼UserManger持有了Activity的引用,當反覆進入Activity的時候,Activity不能被回收,導致了內存泄露,此時看Logcat。

12-20 09:57:07.626 4787-4787/? E/StrictMode: class zhangwan.wj.com.myshare.MainActivity; instances=3; limit=1
          android.os.StrictMode$InstanceCountViolation: class zhangwan.wj.com.myshare.MainActivity; instances=3; limit=1
          at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

明確告訴我們instances=3,說明泄露了3個MainActivity對象

嚴格模式除了可以檢測Activity的內存泄露之外,還能自定義檢測類的對象泄露。這個從從API 11 開始。

public
StrictMode.VmPolicy.Builder setClassInstanceLimit (Class klass, int instanceLimit)

比如,檢測UserManger有沒有泄露,可以這麼寫。

StrictMode.setVmPolicy(new VmPolicy.Builder().setClassInstanceLimit(UserManger.class1).penaltyLog().build());

在比如detectLeakedClosableObjects() 和 detectLeakedSqlLiteObjects(),資源沒有正確關閉時回觸發,detectLeakedRegistrationObjects() 用來檢查 BroadcastReceiver 或者 ServiceConnection 註冊類對象是否被正確釋放等。

StrictMode有個更直接的辦法,在部分手機上,可以在開發者選項中開啓嚴格模式,開啓之後,如果主線程中有執行時間長的操作,屏幕則會閃爍。OKStrictMode比較簡單,到此結束!

參考鏈接:
官網文檔:http://developer.android.com/reference/android/os/StrictMode.html
http://blog.csdn.net/meegomeego/article/details/45746721

Please accept mybest wishes for your happiness and success !

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