Android StrictMode 简介

概述

-StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题。
提供了各种策略,比如监视网络访问 和 磁盘读写操作.当开发者违背了事先设定的规则的时候,就会通过不同的方法去提醒开发者改善代码.
这样的话,就可以帮助我们发现并定位问题,做到有的放矢,更快的解决问题.

官方文档:
developer.android.com/reference/android/os/StrictMode.html

什么是StrictMode

翻译过来就是严格模式 的意思.

StrictMode的作用
在Android开发中,通常都会涉及到网络访问,数据库读写,磁盘读写操作.
在4.0之前,我们是可以在主线程(UI线程)来进行网络访问的,4.0之后,如果在主线程访问网络,那么系统就会给我们抛出一个异常(NetworkOnMainThreadException)
但是,像数据库操作和磁盘操作,等耗时任务则没有严格的限制,而这些则是很影响一款app性能和运行速度的
使用StrictMode.系统会检测出主线程中违例情况并做出反应,如日志打印,弹出对话框等.

StrictMode的使用

我们通可以在 自定义的Application类或者Activity以及其他组件的onCreate()方法中启动 StrictMode,为了能够全局检测应用中的问题,建议放在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();
 }

ps:注意:

  • 因为会提醒用户,所以严格模式需要在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);

你可能感兴趣的:(android,性能优化,StrictMode)