概述
- StrictMode类自Android 2.3 (API 9)引入,用于监测程序中的违例情况。
- 提供了各种策略,比如监视网络访问 和 磁盘读写操作.当开发者违背了事先设定的规则的时候,就会通过不同的方法去提醒开发者改善代码.
官方文档:
developer.android.com/reference/android/os/StrictMode.html
概念
翻译过来就是严格模式 的意思。
4.0之后,如在主线程进行访问网络,则会抛NetworkOnMainThreadException异常,但在4.0之前,android允许主线程(UI线程)网络访问。
而且,像数据库操作和磁盘操作,等耗时任务则没有严格的限制。
使用StrictMode.系统则会监测这些违例并做出反应,如日志打印,弹出对话框等.
使用
可以放在Application或者Activity的onCreate()中,为了全面监测,建议放在Application中。
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyDialog() //弹出违规提示对话框
.penaltyLog() //在Logcat 中打印违规异常信息
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
注意:
- 因为会提醒用户,所以严格模式需要在debug 模式下开启
- 严格模式在Android2.3中引入的,在API>=9后才可以使用
监控策略
严格模式主要检测两大问题,一个是线程策略,即TreadPolicy
,主要用于监测主线程中耗时操作.
另一个是VM策略,即VmPolicy
,主要用于发现内存问题
ThreadPolicy
detectCustomSlowCalls()
.监测自定义的耗时操作
detectDiskReads()
.监测磁盘读取操作
detectDiskWrites()
.监测磁盘写入操作
detectNetwork()
.监测网络操作
permitCustomSlowCalls()
、permitDiskReads()
、permitDiskWrites()
、permitNetwork()
对应的用于关闭某一项的监测.
VmPolicy
detectActivityLeaks()
用于监测Activity泄露
detectLeakedClosableObjects()
用于监测未关闭的Closable对象泄露
detectLeakedSqlLiteObjects()
用于监测泄露的Sqlite对象
setClassInstanceLimit()
用于检测实例数量
通知开发者
一般以penalty
开头的方法都是用做提示功能的.
penaltyDeath()
,当触发违规条件时,直接Crash掉当前应用程序。
penaltyDeathOnNetwork()
,当触发网络违规时,Crash掉当前应用程序。
penaltyDialog()
,触发违规时,显示对违规信息对话框。
penaltyFlashScreen()
,会造成屏幕闪烁,不过一般的设备可能没有这个功能。
penaltyDropBox()
,将违规信息记录到 dropbox 系统日志目录中(/data/system/dropbox)
如以下代码,运行后
/**
* Created by BoBoMEe on 2015/12/29.
*/
public class StrictModeSample extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDialog() //弹出违规提示对话框
.penaltyLog() //在Logcat 中打印违规异常信息
.build());
test();
}
private void test() {
URL url = null;
try {
url = new URL("http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&day=0&city=&dfc=1&charset=utf-8");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String lines = null;
StringBuffer sb = new StringBuffer();
while ((lines = reader.readLine()) != null) {
sb.append(lines);
}
writeToFile(sb);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeToFile(StringBuffer sb) {
FileWriter fw = null;
try {
File externalStorage = Environment.getExternalStorageDirectory();
File destFile = new File(externalStorage, "dest.txt");
fw = new FileWriter(destFile);
fw.write(sb.toString());
fw.close();
} catch (IOException e) {
e.printStackTrace();
} /*finally {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
}
}
运行上面的代码 ,会报出一下错误.
D/StrictMode: StrictMode policy violation; ~duration=2871 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=63 violation=2
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1135)
同时,程序会弹出一个对话框.提示开发者来修改代码.通过log信息,我们可以知道是因为耗时的磁盘操作造成的
detectCustomSlowCalls()
监测UI线程调用的那些方法执行得比较慢,需要和StrictMode.noteSlowCall
配合使用
private void test() {
StrictMode.noteSlowCall("test");
new Thread() {
@Override
public void run() {
}
}.start();
}
会得到类似的信息
D/StrictMode(1349): StrictMode policy violation; **~duration=2019 ms:
我们可以从信息中看到某个方法执行了多长时间,但是这个时间一般是要比实际值要高那么一点点的.
常见解决办法
- 网络操作采用Handler传递消息
- SharedPreferences采用apply等
- 耗时操作放到子线程中来完成
内存泄露监测
private void test() {
new Thread() {
@Override
public void run() {
URL url = null;
try {
url = new URL("http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&day=0&city=&dfc=1&charset=utf-8");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String lines = null;
StringBuffer sb = new StringBuffer();
while ((lines = reader.readLine()) != null) {
sb.append(lines);
}
// writeToFile(sb);
handler.sendEmptyMessage(001);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(StrictModeSample.this, "success", Toast.LENGTH_LONG).show();
}
};
当我们反复旋转屏幕,会得到类似下面的log信息
E/StrictMode(4784): class com.android.StrictModeSample ; instances=4; limit=1
这句话的意思是存在4个实例,而只允许一个,就是我们的Activity发生了泄露了,这时候,我们就要看一下代码了,在上面这段代码中handler持有外部类Activity的强应用,
StrictMode除了可以检测Activity的内存泄露之外,从API 11 开始,还能自定义检测类的实例泄露。
public StrictMode.VmPolicy.Builder setClassInstanceLimit (Class klass, int instanceLimit)
常见解决办法
- Handler使用Activity的若引用的方法来避免泄露
- 数据库操作完毕,要关闭Cursor,及时关闭Closable对象
- 在 onDestroy() 方法中将 receiver 释放掉
unregisterReceiver(this.receiver);