Android严苛模式StrictMode使用详解

StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的。举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框。为了在开发中发现这些容易忽略的问题,我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。

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

StrictMode具体能检测什么

严苛模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy。

ThreadPolicy线程策略检测

  • 线程策略检测的内容有
  • 自定义的耗时调用 使用detectCustomSlowCalls()开启
  • 磁盘读取操作 使用detectDiskReads()开启
  • 磁盘写入操作 使用detectDiskWrites()开启
  • 网络操作 使用detectNetwork()开启

VmPolicy虚拟机策略检测

  • Activity泄露 使用detectActivityLeaks()开启
  • 未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
  • 泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
  • 检测实例数量 使用setClassInstanceLimit()开启

工作原理

其实StrictMode实现原理也比较简单,以IO操作为例,主要是通过在open,read,write,close时进行监控。libcore.io.BlockGuardOs文件就是监控的地方。以open为例,如下进行监控。

@Override
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
 BlockGuard.getThreadPolicy().onReadFromDisk();
 if ((mode & O_ACCMODE) != O_RDONLY) {
  BlockGuard.getThreadPolicy().onWriteToDisk();
 }
 return os.open(path, flags, mode);
}

其中onReadFromDisk()方法的实现,代码位于StrictMode.java中。

public void onReadFromDisk() {
 if ((mPolicyMask & DETECT_DISK_READ) == 0) {
  return;
 }
 if (tooManyViolationsThisLoop()) {
  return;
 }
 BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
 e.fillInStackTrace();
 startHandlingViolationException(e);
}

常见用法

严格模式的开启可以放在Application或者Activity以及其他组件的onCreate方法。为了更好地分析应用中的问题,建议放在Application的onCreate方法中。

其中,我们只需要在app的开发版本下使用 StrictMode,线上版本避免使用 StrictMode,这里定义了一个布尔值变量DEV_MODE来进行控制。

private boolean DEV_MODE = true;
 public void onCreate() {
  if (DEV_MODE) {
   StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
     .detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode
     .detectDiskReads()
     .detectDiskWrites()
     .detectNetwork() // or .detectAll() for all detectable problems
     .penaltyDialog() //弹出违规提示对话框
     .penaltyLog() //在Logcat 中打印违规异常信息
     .penaltyFlashScreen() //API等级11
     .build());
   StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
     .detectLeakedSqlLiteObjects()
     .detectLeakedClosableObjects() //API等级11
     .penaltyLog()
     .penaltyDeath()
     .build());
  }
  super.onCreate();
 }

其中Android3.0引入的方法包括detectCustomSlowCalls()和noteSlowCode(),它们都是用来检测应用中执行缓慢代码的或者潜在的缓慢代码。

查看报告结果

严格模式有很多种报告违例的形式,但是想要分析具体违例情况,还是需要查看日志,终端下过滤StrictMode就能得到违例的具体stacktrace信息。

adb logcat | grep StrictMode

当然也可以选择弹窗形式来简明提醒开发者

弹窗警告

ThreadPolicy 详解

StrictMode.ThreadPolicy.Builder 主要方法如下

detectNetwork() 用于检查UI线程中是否有网络请求操作

检测UI线程中网络请求案例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 Button btnTest;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectNetwork()
    .penaltyLog()
    .build());
  btnTest = (Button) findViewById(R.id.btn_test);
  btnTest.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
  int id = v.getId();
  switch (id) {
   case R.id.btn_test:
    postNetwork();
   break;
  }
 }
 /**
  * 网络连接的操作
  */
 private void postNetwork() {
  try {
   URL url = new URL("http://www.wooyun.org");
   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);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

运行后,触发的警告如下

detectDiskReads() 和 detectDiskWrites() 是磁盘读写检查

磁盘读写检查案例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 Button btnTest;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectDiskWrites()
    .detectDiskReads()
    .penaltyLog()
    .build());
  btnTest = (Button) findViewById(R.id.btn_test);
  btnTest.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
  int id = v.getId();
  switch (id) {
   case R.id.btn_test:
    writeToExternalStorage();
    break;
  }
 }
 /**
  * 文件系统的操作
  */
 public void writeToExternalStorage() {
  File externalStorage = Environment.getExternalStorageDirectory();
  File mbFile = new File(externalStorage, "castiel.txt");
  try {
   OutputStream output = new FileOutputStream(mbFile, true);
   output.write("www.wooyun.org".getBytes());
   output.flush();
   output.close();
  } catch (FileNotFoundException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

运行后,触发的警告如下

noteSlowCall针对执行比较耗时的检查

StrictMode从 API 11开始允许开发者自定义一些耗时调用违例,这种自定义适用于自定义的任务执行类中,比如我们有一个进行任务处理的类,为TaskExecutor。

public class TaskExecutor {
 public void execute(Runnable task) {
  task.run();
 }
}

先需要跟踪每个任务的耗时情况,如果大于500毫秒需要提示给开发者,noteSlowCall就可以实现这个功能,如下修改代码

public class TaskExecutor {
 private static long SLOW_CALL_THRESHOLD = 500;
 public void executeTask(Runnable task) {
  long startTime = SystemClock.uptimeMillis();
  task.run();
  long cost = SystemClock.uptimeMillis() - startTime;
  if (cost > SLOW_CALL_THRESHOLD) {
   StrictMode.noteSlowCall("slowCall cost=" + cost);
  }
 }
}

执行一个耗时2000毫秒的任务

TaskExecutor executor = new TaskExecutor();
executor.executeTask(new Runnable() {
 @Override
 public void run() {
  try {
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
});

得到的违例日志,注意其中~duration=20 ms并非耗时任务的执行时间,而我们的自定义信息msg=slowCall cost=2000才包含了真正的耗时。

penaltyDeath(),当触发违规条件时,直接Crash掉当前应用程序。

penaltyDeathOnNetwork(),当触发网络违规时,Crash掉当前应用程序。

penaltyDialog(),触发违规时,显示对违规信息对话框。

penaltyFlashScreen(),会造成屏幕闪烁,不过一般的设备可能没有这个功能。

penaltyDropBox(),将违规信息记录到 dropbox 系统日志目录中(/data/system/dropbox),你可以通过如下命令进行插件:

adb shell dumpsys dropbox dataappstrictmode --print

permitCustomSlowCalls()、permitDiskReads ()、permitDiskWrites()、permitNetwork: 如果你想关闭某一项检测,可以使用对应的permit*方法。

VMPolicy 详解

StrictMode.VmPolicy.Builder 主要方法如下

detectActivityLeaks() 用户检查 Activity 的内存泄露情况

内存泄露检查案例:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectActivityLeaks()
    .penaltyLog()
    .build()
  );
  new Thread() {
   @Override
   public void run() {
    while (true) {

     SystemClock.sleep(1000);
    }
   }
  }.start();
 }
}

我们反复旋转屏幕就会输出提示信息(重点在 instances=2; limit=1 这一行)

这时因为,我们在Activity中创建了一个Thread匿名内部类,而匿名内部类隐式持有外部类的引用。而每次旋转屏幕是,Android会新创建一个Activity,而原来的Activity实例又被我们启动的匿名内部类线程持有,所以不会释放,从日志上看,当先系统中该Activty有4个实例,而限制是只能创建1各实例。我们不断翻转屏幕,instances 的个数还会持续增加。

detectLeakedClosableObjects()用于资源没有正确关闭时提醒

// 资源引用没有关闭检查案例
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectLeakedClosableObjects()
    .penaltyLog()
    .build()
  );
  File newxmlfile = new File(Environment.getExternalStorageDirectory(), "castiel.txt");
  try {
   newxmlfile.createNewFile();
   FileWriter fw = new FileWriter(newxmlfile);
   fw.write("猴子搬来的救兵WooYun");
   //fw.close(); 我们在这里特意没有关闭 fw
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

运行后触发警告如下

  • detectLeakedSqlLiteObjects() 和
  • detectLeakedClosableObjects()的用法类似,只不过是用来检查 SQLiteCursor 或者 其他 SQLite
  • 对象是否被正确关闭
  • detectLeakedRegistrationObjects() 用来检查 BroadcastReceiver 或者
  • ServiceConnection 注册类对象是否被正确释放
  • setClassInstanceLimit(),设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露

检测内存泄露案例

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 private static class CastielClass{}
 private static List<CastielClass> classList;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  classList = new ArrayList<CastielClass>();
  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .setClassInstanceLimit(CastielClass.class, 2)
    .penaltyLog()
    .build());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
  classList.add(new CastielClass());
 }
}

运行后触发警告如下

其他操作

除了通过日志查看之外,我们也可以在开发者选项中开启严格模式,开启之后,如果主线程中有执行时间长的操作,屏幕则会闪烁,这是一个更加直接的方法。

注意事项

  • 只在开发阶段启用StrictMode,发布应用或者release版本一定要禁用它。
  • 严格模式无法监控JNI中的磁盘IO和网络请求。
  • 应用中并非需要解决全部的违例情况,比如有些IO操作必须在主线程中进行。

总结

以上所述是小编给大家介绍的Android严苛模式StrictMode使用详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

您可能感兴趣的文章:

  • 详解Android StrictMode严格模式的使用方法
  • Android StrictMode运行流程(推荐)
  • Android性能调优利器StrictMode应用分析
(0)

相关推荐

  • Android StrictMode运行流程(推荐)

    什么是 StrictMode(严苛模式) strictmode是android在 API9后引入的检测影响app运行流畅性的一种机制,例如我们都知道的主线程中不允许有网络操作这条规则就是严苛模式规则的一种. strictmode.java 这个类中设定了许多detect标志位例如 DETECT_NETWORK ,还有许多 penalty标志位例如 PENALTY_NETWORK , DETECT标志位决定strictmode是否要对这项内容进行检测,PENALTY标志位决定了在这项内容发生时是否

  • 详解Android StrictMode严格模式的使用方法

    Android 2.3提供一个称为严苛模式StrictMode的调试特性,Google称该特性已经使数百个Android上的Google应用程序受益.那它都做什么呢?它将报告与线程及虚拟机相关的策略违例.一旦检测到策略违例policy violation,你将获得警告,其包含了一个栈trace显示你的应用在何处发生违例.你可以强制用警告代替崩溃crash,也可以仅将警告计入日志让你的应用继续执行.StrictMode是一个十分有用的类,它可以很方便地应用于检查Android应用程序的性能和存在的

  • Android性能调优利器StrictMode应用分析

    作为Android开发,日常的开发工作中或多或少要接触到性能问题,比如我的Android程序运行缓慢卡顿,并且常常出现ANR对话框等等问题.既然有性能问题,就需要进行性能优化.正所谓工欲善其事,必先利其器.一个好的工具,可以帮助我们发现并定位问题,进而有的放矢进行解决.本文主要介绍StrictMode 在Android 应用开发中的应用和一些问题. 什么是StrictMode StrictMode意思为严格模式,是用来检测程序中违例情况的开发者工具.最常用的场景就是检测主线程中本地磁盘和网络读写

  • Android严苛模式StrictMode使用详解

    StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的.举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框.为了在开发中发现这些容易忽略的问题,我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑. 官网文档:http://developer.android.c

  • Android 启动模式FLAG_ACTIVITY_CLEAR_TOP案例详解

    四种启动模式 standard: 只要被启动就会创建一个新的 singleTop: 栈顶复用(当被启动的Activity处于Task栈顶时,可以复用,直接调用onNewIntent方法) singleTask: 栈中复用(被启动的Activity已经处于栈中,会将上边的Activity清除出栈,调用onNewIntent) singleInstance 全局单实例(应用场景:地图,Activity初始化需要大量资源) Intent的标志位FLAG Intent.FLAG_ACTIVITY_SIN

  • Android中mvp模式使用实例详解

    MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller. 在MVC里,View是可以直接访问

  • Android 两种启动模式的实例详解

    Android 两种启动模式的实例详解 Intent的FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT Activity的两种启动模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT 1. 如果已经启动了四个Activity:A,B,C和D.在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里

  • Android中SQLite 使用方法详解

    Android中SQLite 使用方法详解 现在的主流移动设备像android.iPhone等都使用SQLite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到SQLite来存储我们大量的数据,所以我们就需要掌握移动设备上的SQLite开发技巧.对于Android平台来说,系统内置了丰富的API来供开发人员操作SQLite,我们可以轻松的完成对数据的存取. 下面就向大家介绍一下SQLite常用的操作方法,为了方便,我将代码写在了Activity的onCreate中: @Ov

  • Android 活动条ActionBar的详解及实例代码

    Android 活动条ActionBar的详解 图一 图二 图三 图四 图五 ActionBar其提供的功能总结 图一使用ActionBar显示选项菜单项 menu_mainxml代码 启用程序图标导航 如何添加Action View 图二Activity代码区 menu_mainxml代码 clockxml 图三使用ActionBar实现Tab导航 创建ActionBar实现Tab导航步骤 MainActivity代码 DummyFragmentjava代码 图四Android 30之前的Fr

  • Java中的instanceof关键字在Android中的用法实例详解

    在下面介绍Android中如何使用instanceof关键字开发更方便时,先来温习一下java中instanceof的概念. instanceof大部分的概念是这样定义的:instanceof是Java的一个二元操作符,和==,>,<是同一类东西.由于它是由字母组成的,所以也是Java的保留关键字.它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据.举个栗子: String s = "I AM an Object!"; boolean isObj

  • Android 列表选择框 Spinner详解及实例

    Android 列表选择框 Spinner详解及实例 Spinner 是 Android 的列表选择框,不过 spinner 并不需要显示下拉列表,而是相当于弹出一个菜单供用户选择. Spinner 属性: ● android:spinnerMode:列表显示的模式,有两个选择,为弹出列表(dialog)以及下拉列表(dropdown),如果不特别设置,为下拉列表. ● android:entries:使用<string-array.../>资源配置数据源. ● android:prompt:

  • Android原生音量控制实例详解

    本文主要涉及AudioService.还是基于5.1.1版本的代码. AudioService.java文件位于/framework/base/media/java/android/media/下. 音量控制是AudioService最重要的功能之一.先总结一下: AudioService音量管理的核心是VolumeStreamState.它保存了一个流类型所有的音量信息. VolumeStreamState保存了运行时的音量信息,而音量的生效则是在底层AudioFlinger完成的.所以进行音

  • Android加密之全盘加密详解

    前言 Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统. 在 Android 的安全性方面,有很多模块: 1 内核安全性 2 应用安全性 3 应用签名 4 身份验证 5 Trusty TEE 6 SELinux 7 加密 等等 其中,加密又分全盘加密(Android 4.4 引入)和文件级加密(Android 7.0 引入),本文将论述加密中的全盘加密的基本知识.全盘加密在 Android 4.4

随机推荐