溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Android中StrictMode如何使用

發(fā)布時(shí)間:2021-06-28 17:59:20 來(lái)源:億速云 閱讀:130 作者:Leah 欄目:移動(dòng)開(kāi)發(fā)

本篇文章給大家分享的是有關(guān)Android中StrictMode如何使用,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話(huà)不多說(shuō),跟著小編一起來(lái)看看吧。

StrictMode具體能檢測(cè)什么

嚴(yán)苛模式主要檢測(cè)兩大問(wèn)題,一個(gè)是線(xiàn)程策略,即TreadPolicy,另一個(gè)是VM策略,即VmPolicy。

ThreadPolicy線(xiàn)程策略檢測(cè)

  • 線(xiàn)程策略檢測(cè)的內(nèi)容有

  • 自定義的耗時(shí)調(diào)用 使用detectCustomSlowCalls()開(kāi)啟

  • 磁盤(pán)讀取操作 使用detectDiskReads()開(kāi)啟

  • 磁盤(pán)寫(xiě)入操作 使用detectDiskWrites()開(kāi)啟

  • 網(wǎng)絡(luò)操作 使用detectNetwork()開(kāi)啟

VmPolicy虛擬機(jī)策略檢測(cè)

  • Activity泄露 使用detectActivityLeaks()開(kāi)啟

  • 未關(guān)閉的Closable對(duì)象泄露 使用detectLeakedClosableObjects()開(kāi)啟

  • 泄露的Sqlite對(duì)象 使用detectLeakedSqlLiteObjects()開(kāi)啟

  • 檢測(cè)實(shí)例數(shù)量 使用setClassInstanceLimit()開(kāi)啟

工作原理

       其實(shí)StrictMode實(shí)現(xiàn)原理也比較簡(jiǎn)單,以IO操作為例,主要是通過(guò)在open,read,write,close時(shí)進(jìn)行監(jiān)控。libcore.io.BlockGuardOs文件就是監(jiān)控的地方。以open為例,如下進(jìn)行監(jiān)控。

@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()方法的實(shí)現(xiàn),代碼位于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);
}

常見(jiàn)用法

       嚴(yán)格模式的開(kāi)啟可以放在Application或者Activity以及其他組件的onCreate方法。為了更好地分析應(yīng)用中的問(wèn)題,建議放在Application的onCreate方法中。 

       其中,我們只需要在app的開(kāi)發(fā)版本下使用 StrictMode,線(xiàn)上版本避免使用 StrictMode,這里定義了一個(gè)布爾值變量DEV_MODE來(lái)進(jìn)行控制。

private boolean DEV_MODE = true;
 public void onCreate() {
  if (DEV_MODE) {
   StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
     .detectCustomSlowCalls() //API等級(jí)11,使用StrictMode.noteSlowCode
     .detectDiskReads()
     .detectDiskWrites()
     .detectNetwork() // or .detectAll() for all detectable problems
     .penaltyDialog() //彈出違規(guī)提示對(duì)話(huà)框
     .penaltyLog() //在Logcat 中打印違規(guī)異常信息
     .penaltyFlashScreen() //API等級(jí)11
     .build());
   StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
     .detectLeakedSqlLiteObjects()
     .detectLeakedClosableObjects() //API等級(jí)11
     .penaltyLog()
     .penaltyDeath()
     .build());
  }
  super.onCreate();
 }

       其中Android3.0引入的方法包括detectCustomSlowCalls()和noteSlowCode(),它們都是用來(lái)檢測(cè)應(yīng)用中執(zhí)行緩慢代碼的或者潛在的緩慢代碼。

查看報(bào)告結(jié)果

       嚴(yán)格模式有很多種報(bào)告違例的形式,但是想要分析具體違例情況,還是需要查看日志,終端下過(guò)濾StrictMode就能得到違例的具體stacktrace信息。

adb logcat | grep StrictMode

Android中StrictMode如何使用

當(dāng)然也可以選擇彈窗形式來(lái)簡(jiǎn)明提醒開(kāi)發(fā)者

Android中StrictMode如何使用

彈窗警告

ThreadPolicy 詳解

StrictMode.ThreadPolicy.Builder 主要方法如下

detectNetwork() 用于檢查UI線(xiàn)程中是否有網(wǎng)絡(luò)請(qǐng)求操作

檢測(cè)UI線(xiàn)程中網(wǎng)絡(luò)請(qǐng)求案例:

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;
  }
 }
 /**
  * 網(wǎng)絡(luò)連接的操作
  */
 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();
  }
 }
}

運(yùn)行后,觸發(fā)的警告如下

Android中StrictMode如何使用

detectDiskReads() 和 detectDiskWrites() 是磁盤(pán)讀寫(xiě)檢查

磁盤(pán)讀寫(xiě)檢查案例:

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;
  }
 }
 /**
  * 文件系統(tǒng)的操作
  */
 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();
  }
 }
}

運(yùn)行后,觸發(fā)的警告如下

Android中StrictMode如何使用

noteSlowCall針對(duì)執(zhí)行比較耗時(shí)的檢查

       StrictMode從 API 11開(kāi)始允許開(kāi)發(fā)者自定義一些耗時(shí)調(diào)用違例,這種自定義適用于自定義的任務(wù)執(zhí)行類(lèi)中,比如我們有一個(gè)進(jìn)行任務(wù)處理的類(lèi),為T(mén)askExecutor。

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

       先需要跟蹤每個(gè)任務(wù)的耗時(shí)情況,如果大于500毫秒需要提示給開(kāi)發(fā)者,noteSlowCall就可以實(shí)現(xiàn)這個(gè)功能,如下修改代碼

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);
  }
 }
}

執(zhí)行一個(gè)耗時(shí)2000毫秒的任務(wù)

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

       得到的違例日志,注意其中~duration=20 ms并非耗時(shí)任務(wù)的執(zhí)行時(shí)間,而我們的自定義信息msg=slowCall cost=2000才包含了真正的耗時(shí)。

penaltyDeath(),當(dāng)觸發(fā)違規(guī)條件時(shí),直接Crash掉當(dāng)前應(yīng)用程序。

penaltyDeathOnNetwork(),當(dāng)觸發(fā)網(wǎng)絡(luò)違規(guī)時(shí),Crash掉當(dāng)前應(yīng)用程序。

penaltyDialog(),觸發(fā)違規(guī)時(shí),顯示對(duì)違規(guī)信息對(duì)話(huà)框。

penaltyFlashScreen(),會(huì)造成屏幕閃爍,不過(guò)一般的設(shè)備可能沒(méi)有這個(gè)功能。

penaltyDropBox(),將違規(guī)信息記錄到 dropbox 系統(tǒng)日志目錄中(/data/system/dropbox),你可以通過(guò)如下命令進(jìn)行插件:

adb shell dumpsys dropbox dataappstrictmode --print

permitCustomSlowCalls()、permitDiskReads ()、permitDiskWrites()、permitNetwork: 如果你想關(guān)閉某一項(xiàng)檢測(cè),可以使用對(duì)應(yīng)的permit*方法。

VMPolicy 詳解

StrictMode.VmPolicy.Builder 主要方法如下

detectActivityLeaks() 用戶(hù)檢查 Activity 的內(nèi)存泄露情況

內(nèi)存泄露檢查案例:

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();
 }
}

我們反復(fù)旋轉(zhuǎn)屏幕就會(huì)輸出提示信息(重點(diǎn)在 instances=2; limit=1 這一行)

Android中StrictMode如何使用 

       這時(shí)因?yàn)椋覀冊(cè)贏ctivity中創(chuàng)建了一個(gè)Thread匿名內(nèi)部類(lèi),而匿名內(nèi)部類(lèi)隱式持有外部類(lèi)的引用。而每次旋轉(zhuǎn)屏幕是,Android會(huì)新創(chuàng)建一個(gè)Activity,而原來(lái)的Activity實(shí)例又被我們啟動(dòng)的匿名內(nèi)部類(lèi)線(xiàn)程持有,所以不會(huì)釋放,從日志上看,當(dāng)先系統(tǒng)中該Activty有4個(gè)實(shí)例,而限制是只能創(chuàng)建1各實(shí)例。我們不斷翻轉(zhuǎn)屏幕,instances 的個(gè)數(shù)還會(huì)持續(xù)增加。

detectLeakedClosableObjects()用于資源沒(méi)有正確關(guān)閉時(shí)提醒

// 資源引用沒(méi)有關(guān)閉檢查案例
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("猴子搬來(lái)的救兵WooYun");
   //fw.close(); 我們?cè)谶@里特意沒(méi)有關(guān)閉 fw
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

運(yùn)行后觸發(fā)警告如下

Android中StrictMode如何使用

  • detectLeakedSqlLiteObjects() 和

  • detectLeakedClosableObjects()的用法類(lèi)似,只不過(guò)是用來(lái)檢查 SQLiteCursor 或者 其他 SQLite

  • 對(duì)象是否被正確關(guān)閉

  • detectLeakedRegistrationObjects() 用來(lái)檢查 BroadcastReceiver 或者

  • ServiceConnection 注冊(cè)類(lèi)對(duì)象是否被正確釋放

  • setClassInstanceLimit(),設(shè)置某個(gè)類(lèi)的同時(shí)處于內(nèi)存中的實(shí)例上限,可以協(xié)助檢查內(nèi)存泄露

檢測(cè)內(nèi)存泄露案例

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());
 }
}

運(yùn)行后觸發(fā)警告如下

Android中StrictMode如何使用

其他操作

       除了通過(guò)日志查看之外,我們也可以在開(kāi)發(fā)者選項(xiàng)中開(kāi)啟嚴(yán)格模式,開(kāi)啟之后,如果主線(xiàn)程中有執(zhí)行時(shí)間長(zhǎng)的操作,屏幕則會(huì)閃爍,這是一個(gè)更加直接的方法。

Android中StrictMode如何使用

注意事項(xiàng)

  • 只在開(kāi)發(fā)階段啟用StrictMode,發(fā)布應(yīng)用或者release版本一定要禁用它。

  • 嚴(yán)格模式無(wú)法監(jiān)控JNI中的磁盤(pán)IO和網(wǎng)絡(luò)請(qǐng)求。

  • 應(yīng)用中并非需要解決全部的違例情況,比如有些IO操作必須在主線(xiàn)程中進(jìn)行。

以上就是Android中StrictMode如何使用,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI