×

Android探索之旅 | StrictMode严苛模式

96
程序员联盟
2016.08.02 14:08* 字数 1152

-- 作者 谢恩铭 转载请注明出处

StrictMode简介


StrictMode (android.os.StrictMode) 是一个自Android 2.3版(API 9。Gingerbread,姜饼)引入的类。

StrictMode是Strict和Mode的合并,在英语中,strict表示“严格的”,mode表示“模式”,因此,StrictMode就是“严格的模式”,或叫“严苛模式”。

既然是严苛的,那么肯定是对什么东西有限制。因为严格的老师肯定不会对坏学生纵容的,对吧,所以可以想见StrictMode是用来监测Android中的什么东西。

严苛的老师

是的,聪明如你果然猜对了。StrictMode就是用来指定一系列策略(policy),对相应规则(rule)进行检查并且做出反应。

这些策略大致包括Android的编码规范,例如监控在主线程(UI线程)中的操作,等等。

StrictMode有不同的策略,每种策略又用不同的规则(rule),每种规则又对应不同的方法,一旦规则被违反,这些对应的方法就会被用来做出反应。

注意 :

  • 在Debug模式启用StrictMode,别在Release模式启用。用户的小心脏是很脆弱的,假如一点点违规就导致应用崩溃,或者弹窗,那用户会把你的应用删除了。
  • 特别地, 调用JNI实现的磁盘读写操作和网络操作不会激活StrictMode。

策略类型


目前,有两种类型的策略:

  • Thread Policy : 线程策略应用到特定的线程。
  • VM Policy : VM是Virtual Machine的缩写,表示“虚拟机”,不要搞错以为是Virtual Memory(虚拟内存)。应用于虚拟机进程中的所有线程。

ThreadPolicy.Builder中的一些方法:

  • detectAll() : 侦测一切潜在违规
  • detectCustomSlowCalls() : 侦测自定义的耗时操作
  • detectDiskReads() : 侦测磁盘读
  • detectDiskWrites() : 侦测磁盘写
  • detectNetwork() : 侦测网络操作
  • permitAll() : 禁用所有侦测
  • permitDiskReads() : 允许磁盘读

VmPolicy.Builder中的一些方法 :

  • detectAll() : 侦测一切潜在违规
  • detectActivityLeaks() : 侦测Activity(活动)泄露
  • detectLeakedClosableObjects() : 当显式中止方法调用之后,假如可被Closeable类或其他的对象没有被关闭。

处罚


Penalty是英语“处罚”的意思,所以凡是以penalty开头的方法都表示违规时要做出什么反应。

对于每个策略,我们可以指定多个处罚形式,而处罚也是从最不严重的到最严重(从打印日志到直接crash(崩溃))依次执行。

暂时还没有机制能使监测到的违规与特定的处罚对应。

  • penaltyDeath() : 违规时,直接使应用崩溃。
  • penaltyDialog() : 违规时,向开发者显示一个恼人的Dialog对话框。
  • penaltyLog() : 违规时,将违规信息写入系统日志。

使用


StrictMode使用起来非常简单。

设置策略

你可以在你的Application(应用)或者应用中的Activity的onCreate()方法中设置启用StrictMode的策略。不过为了更全面的监测,最好就放在Application的onCreate()方法中,一劳永逸。

设置StrictMode可以通过setVmPolicy(StrictMode.VmPolicy)或setThreadPolicy(StrictMode.ThreadPolicy)。

setVmPolicy(StrictMode.VmPolicy)或setThreadPolicy(StrictMode.ThreadPolicy)方法的参数是用VmPolicy.Builder或ThreadPolicy.Builder来构建的。

举例:

@Override
public void onCreate() {
    super.onCreate();
 
    // 分别为MainThread和VM设置Strict Mode 
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectDiskReads()
            .detectDiskWrites()
            .detectNetwork()
            .detectResourceMismatches()
            .detectCustomSlowCalls()
            .penaltyDeath()
            .build());

        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .detectLeakedRegistrationObjects()
            .detectActivityLeaks()
            .penaltyDeath()
            .build());
    }
}

扩充StrictMode


1.用getThreadPolicy() 或getVmPolicy()获得当前策略。
2.用setThreadPolicy() or setVmPolicy()来扩充它。

举例:

StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(oldThreadPolicy)
    .permitDiskWrites()  // 在原有策略的规则基础上,不监测读写磁盘
    .build());
 
StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy();
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(oldVmPolicy)
    .detectFileUriExposure()   // 在原有策略的规则基础上,监测文件URI暴露
    .build());

StrictMode的日志形式

09-04 16:15:34.592: DEBUG/StrictMode(15883): StrictMode policy violation; ~duration=319 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
09-04 16:15:34.592: DEBUG/StrictMode(15883):     at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1041)

可以看到,在Logcat中总会有StrictMode开头的Log。因此,我们可以这样查找所有StrictMode的日志:

adb logcat | grep StrictMode

测试实例


写入外部存储

public void writeToExternalStorage() {
    File externalStorage = Environment.getExternalStorageDirectory();
    File destFile = new File(externalStorage, "dest.txt");
    try {
        OutputStream output = new FileOutputStream(destFile, true);
        output.write("coderunity.com".getBytes());
        output.flush();
        output.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Activity泄露


下面的代码,如果我们进入、退出多次LeakyActivity, 则会触发
StrictMode.ThreadPolicy.Builder().detectActivityLeaks() :

public class MyApplication extends Application {
    public static final boolean IS_DEBUG = true;
    public static ArrayList<Activity> sLeakyActivities = new ArrayList<Activity>();
}
 
public class LeakyActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyApplication.sLeakyActivities.add(this);
    }
}

在设置中启用StrictMode


我们也可以在Android设备的设置(Settings)中启用StrictMode:

Settings(设置) -> Developer options(开发者选项),然后开启它。开启之后,一旦应用在主线程中执行耗时操作,屏幕就会闪烁。

在开发者选项中开启StrictMode

人世间,
万千情感皆有温度,
千万代码似有性格。
这里有原创教程,IT丛林......
和你一起探索程序人生。
我是谢恩铭,在巴黎奋斗的嵌入式软件工程师。
个人简介
热爱生活,喜欢游泳,略懂烹饪。
人生格言:“向着标杆直跑”

Android探索之旅
Web note ad 1