Android性能優(yōu)化第(七)篇---StrickMode嚴(yán)苛模式檢測耗時與內(nèi)存問題

版權(quán)聲明:本文為LooperJing原創(chuàng)文章,轉(zhuǎn)載請注明出處!

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

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

  • 1、自定義的耗時調(diào)用 使用detectCustomSlowCalls()開啟
  • 2、磁盤讀取操作 使用detectDiskReads()開啟
  • 3、磁盤寫入操作 使用detectDiskWrites()開啟
  • 4、網(wǎng)絡(luò)操作 使用detectNetwork()開啟

虛擬機(jī)策略(VmPolicy)檢測的內(nèi)容有

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

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

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

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

如何使用嚴(yán)格模式

嚴(yán)格模式的開啟可以放在Application或者Activity以及其他組件的onCreate方法。為了更好地分析應(yīng)用中的問題,建議放在Application的onCreate方法中。設(shè)置一次就夠了。

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()//彈出違規(guī)提示框
                .penaltyLog()//在Logcat中打印違規(guī)日志
                .build());
        requestDataFromNet();
    }

    /**
     * 請求數(shù)據(jù)
     */
    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的日志,是在主線程中做了訪問網(wǎng)絡(luò)的操作。

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)

上面分析了是異步處理相關(guān)的例子,現(xiàn)在分析一個內(nèi)存管相關(guān)的例子,比如檢測內(nèi)存泄露檢測,用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中打印違規(guī)日志
                .build());

        UserManger instance = UserManger.getInstance(this);
    }

}

比如上面的代碼UserManger持有了Activity的引用,當(dāng)反復(fù)進(jìn)入Activity的時候,Activity不能被回收,導(dǎo)致了內(nèi)存泄露,此時看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對象

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

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

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

StrictMode.setVmPolicy(new VmPolicy.Builder().setClassInstanceLimit(UserManger.class, 1).penaltyLog().build());

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

StrictMode有個更直接的辦法,在部分手機(jī)上,可以在開發(fā)者選項中開啟嚴(yán)格模式,開啟之后,如果主線程中有執(zhí)行時間長的操作,屏幕則會閃爍。OKStrictMode比較簡單,到此結(jié)束!

參考鏈接:
官網(wǎng)文檔: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 !

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容