StrictMode ——Android性能調優的利器

      網友投稿 809 2022-05-28

      性能無外乎就是CPU密集型或I/O密集型兩種。

      StrictMode是一個開發者工具,常用于捕獲在應用主線程中發生的磁盤I/O、網絡訪問違例等問題。

      StrictMode具體能檢測什么呢

      StrictMode主要檢測兩大問題:線程策略(TreadPolicy)和VM策略(VmPolicy)。

      ThreadPolicy線程策略:

      自定義的耗時調用,使用detectCustomSlowCalls()開啟;

      磁盤讀取操作,使用detectDiskReads()開啟;

      磁盤寫入操作,使用detectDiskWrites()開啟;

      網絡操作,使用detectNetwork()開啟。

      VmPolicy虛擬機策略:

      Activity泄漏,使用detectActivityLeaks()開啟;

      未關閉的Closable對象泄漏,使用detectLeakedClosableObjects()開啟;

      泄漏的Sqlite對象,使用detectLeakedSqlLiteObjects()開啟;

      檢測實例數量,使用setClassInstanceLimit()開啟。

      如何使用呢?

      可以在應用的Application、Activity或者其他應用組件的onCreate方法中加入檢測代碼,如:

      public void onCreate() { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate(); }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      如果觀測結果呢?

      StrictMode有多種報告違例的形式,但要分析具體違例情況,還是需要查看日志。我們在此介紹兩種方式,一種是在android studio IDE的logcat里查看:

      另一種是在終端下,過濾StrictMode得到違例的具體stacktrace信息(手機要打開調試用的app),然后打開命令終端,使用adb命令來查看:

      ~$ adb logcat | grep StrictMode

      1

      如果發現有違例的行為,可以通過使用線程(threads)、Handler、AsyncTask、IntentService等幫助解決。提供一下些常用的措施:

      假如在主線程中進行文件讀寫出現了違例,可用工作線程(另外開辟子線程)來解決,必要時還可以結合Handler一起來解決。

      SharedPreferences的寫入操作,在API 9以上應該優先使用apply而非commit。

      如果是存在未關閉的Closable對象(如有些流OutputStream,在出現異常時,未來得及關閉),根據對應的stacktrace進行關閉。

      如果是SQLite對象泄漏,根據對應的stacktrace進行釋放。

      接下來我們來舉個在主線程中的文件寫入,引起違例警告的例子:

      1.首先Activity的onCreate方法中加上檢測代碼:

      注:以下的代碼啟用全部的ThreadPolicy和VmPolicy違例檢測

      StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());

      1

      2

      3

      4

      2.這是引起違例的代碼:

      public void writeToExternalStorageInMainThread() { File externalStorage = Environment.getExternalStorageDirectory(); File destFile = new File(externalStorage, "hello.txt"); try { OutputStream output = new FileOutputStream(destFile, true); output.write("I am testing io".getBytes()); output.flush(); output.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      3.運行app,觀察logcat的輸出,下面是部分截圖:

      logcat已告訴我們出現了違例和出現的位置。

      4.解決這個違例

      修改一下writeToExternalStorageInMainThread方法,將引起違例的代碼都放在一個工作線程中去執行,如下所示:

      public void writeToExternalStorageInMainThread() { new Thread(new Runnable() { @Override public void run() { File externalStorage = Environment.getExternalStorageDirectory(); File destFile = new File(externalStorage, "hello.txt"); OutputStream output = null; try { output = new FileOutputStream(destFile, true); output.write("I am testing io".getBytes()); output.flush(); output.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(output != null){ try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } } }).start(); }

      StrictMode ——Android性能調優的利器

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      demo示例

      檢測內存泄漏

      通常情況下,檢測內存泄漏,我們會使用MAT(Eclipse Memory Analyzer)工具對heap dump 文件進行分析。但是使用StrictMode,只需要過濾日志就能發現內存泄漏,更快捷方便。

      1.首先,需要開啟對檢測Activity泄漏的違例檢測,可以使用detectAll或者detectActivityLeaks():

      StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectActivityLeaks().penaltyLog().build());

      1

      2.寫一段能夠產生Activity泄漏的代碼

      public class LeakActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); if(MyApplication.IS_DEBUG){ MyApplication.sLeakyActivities.add(this); } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      3.MyApplication中關于sLeakyActivities的部分實現

      public class MyApplication extends Application { public static final boolean IS_DEBUG = true; public static ArrayList sLeakyActivities = new ArrayList(); @Override public void onCreate() { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectActivityLeaks().penaltyLog().build()); super.onCreate(); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      4.引發內存泄漏的操作:

      通過不斷從MainActivity打開LeakActivity,再返回,再打開,如此反復操作,引發內存泄漏,下面是MainActivity的代碼:

      public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView)findViewById(R.id.tv); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,LeakActivity.class)); } }); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      5.當我們反復進入LeakyActivity再退出,在Logcat中過濾StrictMode就會得到這樣的日志:

      2019-04-04 19:49:43.502 32708-32708/com.wong.appmemoryleakydemo E/StrictMode: class com.wong.appmemoryleakydemo.LeakActivity; instances=7; limit=1 android.os.StrictMode$InstanceCountViolation: class com.wong.appmemoryleakydemo.LeakActivity; instances=7; limit=1 at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

      1

      2

      3

      分析日志:LeakyActivity本應該只存在一個實例的,但現在存在了7個,說明LeakyActivity發生了內存泄漏。

      檢測內存泄漏demo

      自定義檢測類的實例泄漏

      我們還可以通過StrictMode自定義檢測類的實例泄漏。從API 11 開始,系統提供的這個方法setClassInstanceLimit可以實現我們的需求。比如說有個類叫SingleAction.class,我們認為在運行時,它應該只有一個實例,如果多一個,我們就可以認為發生了內存泄漏:

      1.開啟違例檢測,如下:

      StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().setClassInstanceLimit(SingleAction.class,1).penaltyLog().build());

      1

      上面代碼就是說,當SingleAction類出現多于一個實例時,就報告內存泄漏。

      耗時調用違例:noteSlowCall

      StrictMode從API 11開始允許開發者自定義一些耗時調用違例,這種自定義適用于自定義的任務執行類中,比如自定義任務處理類MyTaskExecutor:

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

      1

      2

      3

      4

      5

      但是如果我們想跟蹤每個任務執行的耗時情況,如果耗時大于500毫秒就通知我們,我們該怎么辦呢?StrictMode的noteSlowCall方法可以實現這個功能,修改MyTaskExecutor,如下所示:

      public class MyTaskExecutor { public static long CAN_BEAR_TIME = 500; public void execute(Runnable task){ long sTime = SystemClock.uptimeMillis(); task.run(); long cTime = SystemClock.uptimeMillis() - sTime; if(cTime > CAN_BEAR_TIME){ StrictMode.noteSlowCall("slow call cost:"+cTime); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      執行一個耗時1000毫秒的任務,測試一下:

      package com.wong.timeconsumingviolation; import android.os.StrictMode; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyTaskExecutor taskExecutor = new MyTaskExecutor(); taskExecutor.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      在Logcat中過濾StrictMode就會得到這樣的日志:

      2019-04-04 20:27:27.020 6147-6147/com.wong.timeconsumingviolation D/StrictMode: StrictMode policy violation; ~duration=29 ms: android.os.StrictMode$StrictModeCustomViolation: policy=65599 violation=8 msg=slow call cost:1001 at android.os.StrictMode$AndroidBlockGuardPolicy.onCustomSlowCall(StrictMode.java:1397) at android.os.StrictMode.noteSlowCall(StrictMode.java:2340) at com.wong.timeconsumingviolation.MyTaskExecutor.execute(MyTaskExecutor.java:19) at com.wong.timeconsumingviolation.MainActivity.onCreate(MainActivity.java:17) at android.app.Activity.performCreate(Activity.java:7040) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2809) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2931) at android.app.ActivityThread.-wrap11(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1620) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:173) at android.app.ActivityThread.main(ActivityThread.java:6698) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      從日志分析來看,我們已成功跟蹤到耗時超過500ms的任務了。

      注意上面的日志結果中的:duration=29 ms,并非耗時任務的執行時間,而我們自定義信息msg=slow call cost:1001包包含的時間才是真正的耗時。

      自定義耗時違例demo

      注意

      StrictMode無法監控JNI中的磁盤IO和網絡請求。

      應用中并非需要解決全部的違例情況,比如有些IO操作必須在主線程中進行。

      通常情況下StrictMode給出的耗時相對實際情況偏高,并不是真正的耗時數據。

      Android 應用性能調優

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Python實現Linux監控
      下一篇:揭秘華為云DLI背后的核心計算引擎
      相關文章
      亚洲国产精品久久久久秋霞小| 亚洲制服在线观看| 亚洲大香人伊一本线| 亚洲AV午夜福利精品一区二区| 中文字幕亚洲一区二区va在线| 亚洲精品tv久久久久| 亚洲国产黄在线观看| 亚洲国产成人久久综合一区77| 亚洲国产成人影院播放| 国产亚洲精彩视频| 亚洲国产主播精品极品网红| 亚洲国模精品一区| 亚洲午夜精品久久久久久浪潮| 亚洲欧洲久久av| 中文字幕人成人乱码亚洲电影| 在线观看亚洲精品福利片| 亚洲中文字幕无码不卡电影| 亚洲综合精品香蕉久久网| 亚洲熟妇无码另类久久久| 国产亚洲精久久久久久无码77777 国产亚洲精品成人AA片新蒲金 | 国产精品亚洲片在线| 亚洲日韩乱码中文无码蜜桃臀网站| 国产精品亚洲高清一区二区| 亚洲一区二区女搞男| 久久精品国产亚洲av麻豆| 亚洲一区综合在线播放| 亚洲精品日韩中文字幕久久久| 亚洲熟妇色自偷自拍另类| 亚洲成在人线电影天堂色| 亚洲伊人久久大香线蕉| 国产亚洲精品成人AA片| 亚洲Av永久无码精品一区二区| 男人的天堂亚洲一区二区三区 | 亚洲性猛交XXXX| 亚洲动漫精品无码av天堂| 亚洲综合婷婷久久| 亚洲av午夜精品无码专区| 亚洲午夜福利在线视频| 日本亚洲高清乱码中文在线观看| 亚洲成av人片天堂网老年人| 国产国拍精品亚洲AV片|