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.class, 1).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 !