An overview of the

StrictMode is an essential performance check tool for Android development. StrictMode can help detect code blocks that do not make sense.

Classification of strategy

StrictMode is divided into ThreadPolicy and VmPolicy.

ThreadPolicy

Threading strategy mainly includes the following aspects

  • DetectNetwork: Detection of mainline network usage (critical)
  • DetectCustomSlowCalls: Detect custom slower-running functions
  • PenaltyLog: outputs logs
  • PenaltyDialog: Pop-up dialog box to monitor the situation
  • DetectDiskReads: detectDiskReads on THE UI thread (critical)
  • DetectDiskWrites: detectDiskWrites in the UI thread (critical)
  • DetectResourceMismatches: Detecting resource mismatches (API >22)
  • DetectAll: All supported items such as detectAll (if you are too lazy to list them all, you can do this)
  • PermitDiskReads: allows the UI thread to read operations on disk

VmPolicy (VmPolicy)

Vm policies include the following aspects

  • Detectacvityleaks: Detecting Activity memory leaks (important) (API >10)
  • DetectCleartextNetwork: plaintext network (API >22)
  • DetectFileUriExposure: detection of file:// or content:// (API >17)
  • DetectLeakedClosableObjects: testing resources not properly closed (important) (API > 10)
  • DetectLeakedRegistrationObjects: BroadcastReceiver and ServiceConnection whether be released (important) (API > 15)
  • DetectLeakedSqlLiteObjects: database resource is not closed properly. (important) (API > 8)
  • SetClassInstanceLimit: Set the maximum number of instances of a class that are in memory at the same time to help check for memory leaks (important)
  • PenaltyLog: outputs logs
  • PenaltyDeath: App crashes once detected

code

Private void enabledStrictMode () {/ / open Thread strategy mode StrictMode. SetThreadPolicy (new StrictMode. ThreadPolicy. Builder (.) detectNetwork () / / monitoring the main thread to use network IO detectCustomSlowCalls () / / monitoring custom slow function. DetectDiskReads () DetectDiskWrites (); penaltyLog(); penaltyDialog(); build(); / / open the VM strategy pattern StrictMode. SetVmPolicy (new StrictMode. VmPolicy. Builder () detectLeakedSqlLiteObjects sqlite leak () / / monitoring DetectLeakedClosableObjects () / / monitoring not closing IO object. SetClassInstanceLimit (MainActivity class, 1) / / set a class instance in memory at the same time limit, Detectacvityleaks (). PenaltyLog ()// Write to the log. PenaltyDeath ()// the case is terminated. Build ()); }Copy the code

Case 1

public class MainActivity extends Activity { private Handler mHandler = new Handler(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (BuildConfig.DEBUG) { enabledStrictMode(); } mhandler.postdelayed (new Runnable() {@override public void run() {log.d ("MainActivity", "I'm coming "); }}, 10 * 1000); TextView tv = new TextView(this); Tv.settext (" Nice "); } private void enabledStrictMode () {/ / open Thread strategy mode StrictMode. SetThreadPolicy (new StrictMode. ThreadPolicy. Builder (.) detectNetwork () / / monitoring the main thread to use network IO detectCustomSlowCalls () / / monitoring custom slow function. DetectDiskReads () DetectDiskWrites (); penaltyLog(); penaltyDialog(); build(); / / open the VM strategy pattern StrictMode. SetVmPolicy (new StrictMode. VmPolicy. Builder () detectLeakedSqlLiteObjects sqlite leak () / / monitoring DetectLeakedClosableObjects () / / monitoring not closing IO object. SetClassInstanceLimit (MainActivity class, 1) / / set a class instance in memory at the same time limit, Detectacvityleaks (). PenaltyLog ()// Write to the log. PenaltyDeath ()// the case is terminated. Build ()); }}Copy the code

As shown in the code, I create a Handler (non-static) in the MainActivity (singleTask and app startup Activity) and execute a task delayed by 10 seconds. Now I keep starting and exiting MainActivity, and I find something like this

As you can see, MainActivity creates multiple instances (this diagram uses MAT’s OQL, as explained in more detail in a later section), and we expect only one instance of MainActivity. List one of the object instance reference paths, as shown in the figure below.

As you can see from the above figure, it is the Handler that holds the MainActivity instance, causing the MainActivity to fail to be released.

transform

public class MainActivity extends Activity { private static class InnerHandler extends Handler { private final WeakReference<MainActivity> mWeakreference; InnerHandler(MainActivity activity) { mWeakreference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); final MainActivity activity = mWeakreference.get(); if (activity == null) { return; } log.d ("MainActivity"," MSG "); } } private Handler mHandler = new InnerHandler(this); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (BuildConfig.DEBUG) { enabledStrictMode(); } mhandler.postdelayed (new Runnable() {@override public void run() {log.d ("MainActivity", "I'm coming "); }}, 10 * 1000); TextView tv = new TextView(this); Tv.settext (" I'm coming "); setContentView(tv); } @Override protected void onDestroy() { mHandler.removeCallbacksAndMessages(null); super.onDestroy(); } private void enabledStrictMode () {/ / open Thread strategy mode StrictMode. SetThreadPolicy (new StrictMode. ThreadPolicy. Builder (.) detectNetwork () / / monitoring the main thread to use network IO detectCustomSlowCalls () / / monitoring custom slow function. DetectDiskReads () DetectDiskWrites (); penaltyLog(); penaltyDialog(); build(); / / open the VM strategy pattern StrictMode. SetVmPolicy (new StrictMode. VmPolicy. Builder () detectLeakedSqlLiteObjects sqlite leak () / / monitoring DetectLeakedClosableObjects () / / monitoring not closing IO object. SetClassInstanceLimit (MainActivity class, 1) / / set a class instance in memory at the same time limit, Detectacvityleaks ().penaltylog ()// Write log.build ()); }}Copy the code

Will for a static inner class Handler implementation, and to hold the current Activity, by means of a weak reference at onDestory call removeCallbacksAndMessages (null) method, the complete null, said it would all messages in the Handler to empty out. After running the code, see the following figure through MAT analysis

As can be seen from the figure, there is currently only one MainActivity, as expected by the code design.

note

During our analysis of this case, android instances=2; StrictMode message with limit=1 because the system is collecting the MainActivity instance during the process of starting and exiting the MainActivity. This object is being referenced by FinalizerReference. We are starting another MainActivity, so we report two instances.

Case 2

public class MainActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (BuildConfig.DEBUG) { enabledStrictMode(); } TextView tv = new TextView(this); Tv.settext (" I'm coming "); setContentView(tv); newThread(); takeTime(); } private void newThread() { for (int i = 0; i < 50; i++) { new Thread(new Runnable() { @Override public void run() { takeTime(); } }).start(); } } private void takeTime() { try { File file = new File(getCacheDir(), "test"); if (file.exists()) { file.delete(); } file.createNewFile(); FileOutputStream fileOutputStream = new FileOutputStream(file); Final String content = "Hello, I'm coming "; StringBuffer buffer = new StringBuffer(); for (int i = 0; i < 100; i++) { buffer.append(content); } fileOutputStream.write(buffer.toString().getBytes()); fileOutputStream.flush(); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); } private void enabledStrictMode () {/ / open Thread strategy mode StrictMode. SetThreadPolicy (new StrictMode. ThreadPolicy. Builder (.) detectNetwork () / / monitoring the main thread to use network IO detectCustomSlowCalls () / / monitoring custom slow function. DetectDiskReads () DetectDiskWrites (); penaltyLog(); penaltyDialog(); build(); / / open the VM strategy pattern StrictMode. SetVmPolicy (new StrictMode. VmPolicy. Builder () detectLeakedSqlLiteObjects sqlite leak () / / monitoring DetectLeakedClosableObjects () / / monitoring not closing IO object. SetClassInstanceLimit (MainActivity class, 1) / / set a class instance in memory at the same time limit, Detectacvityleaks ().penaltylog ()// Write log.build ()); }}Copy the code

Run the above code, a warning dialog box pops up

Click OK, check StrictMode log see attached picture

According to the log information, the program spent 75ms on createNewFile, openFile and writeFile, which is a time-consuming operation for the program. Continue with our journal

The file stream is not closed, and we can quickly locate the problem point by logging:

      fileOutputStream.write(buffer.toString().getBytes());
      fileOutputStream.flush();
Copy the code

After the file stream is flushed, the close method is not executed. As a result, the file resource is held by the object and cannot be released, resulting in a waste of memory and resources.

conclusion

StrictMode in addition to the above cases, StrictMode can also detect IO, network, database and other related operations, and these operations are exactly the most common factors affecting App performance in the process of Android development (time-consuming, CPU time, occupy a large amount of memory). Therefore, it is a good habit to pay attention to StrictMode changes in the development process. On the one hand, it can check the quality of the code of the project team members, and on the other hand, it can help me form some good thinking ways of writing code in the process of Android development. In StrictMode, we should always pay attention to the log conversion (such as method execution time), especially those red logs, because these methods cause huge problems.