轉載自:https://blog.csdn.net/brokge/article/details/8543145
Android 2.3提供一個稱爲嚴苛模式(StrictMode)的調試特性,Google稱該特性已經使數百個Android上的Google應用程序受益。那它都做什麼呢?它將報告與線程及虛擬機相關的策略違例。一旦檢測到策略違例(policy violation),你將獲得警告,其包含了一個棧trace顯示你的應用在何處發生違例。你可以強制用警告代替崩潰(crash),也可以僅將警告計入日誌,讓你的應用繼續執行。策略的細節尚難確定,可以期待隨Android的成熟Google將增加更多策略。
目前有2種策略可用,第一個和線程相關,它主要針對主線程(或UI線程)。由於在主線程中讀寫磁盤和進行網絡訪問都不是好的做法,Google已經在磁盤和網絡代碼中添加了嚴苛模式(StrictMode)鉤子(hook)。如果你對某個線程打開嚴苛模式(StrictMode),當那個線程進行磁盤和網絡訪問,你將獲得警告。你可以選擇警告方式。一些違例包含用戶慢速調用(custom slow calls 這麼翻譯行嗎?),磁盤讀寫,網絡訪問。你能選擇將警告寫入LogCat,顯示一個對話框,閃下屏幕,寫入DropBox日誌文件,或讓應用崩潰。最通常的做法是寫入LogCat或讓應用崩潰。列表2-9顯示了一個爲線程策略設置嚴苛模式(StrictMode)的例子。
列表2-9 設置嚴苛模式(StrictMode)的線程策略
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskReads()
- .detectDiskWrites()
- .detectNetwork()
- .penaltyLog()
- .build());
Builder類使得設置變得很簡單,Builder函數定義所有策略都返回Builder對象,從而這些函數能像列表2-9那樣串連在一起。最後調用build()函數返回一個ThreadPolicy對象作爲StrictMode對象的setThreadPolicy()函數的參數。注意到setThreadPolicy()是一個靜態函數,因此不需要實例化StrictMode對象。在內部,setThreadPolicy()將對當前線程應用該策略。如果不指定檢測函數,也可以用detectAll()來替代。penaltyLog()表示將警告輸出到LogCat,你也可以使用其他或增加新的懲罰(penalty)函數,例如使用penaltyDeath()的話,一旦StrictMode消息被寫到LogCat後應用就會崩潰。
你不需要頻繁打開嚴苛模式(StrictMode),你可以在主活動的onCreate()函數中打開它,你也可以在Application派生類的OnCreate()函數中設置嚴苛模式(StrictMode)。線程中運行的任何代碼都可以設置嚴苛模式(StrictMode),但你的確只需要設置一次,一次就夠了。
類似於線程策略(ThreadPolicy),嚴苛模式(StrictMode)有虛擬機策略(VmPolicy)。虛擬機策略(VmPolicy)能檢查內存泄漏,譬如,當關閉一個SQLite對象前的完結操作,或其他任何類似可關閉對象在關閉前的完結操作。虛擬機策略(VmPolicy)由一個類似的Builder類創建,如列表2-10所示。和線程策略(ThreadPolicy)不同的是,虛擬機策略(VmPolicy)不能通過一個對話框提供警告。
列表2-10 設置嚴苛模式(StrictMode)的虛擬機策略
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDeath()
- .build());
因爲設置發生在線程中,嚴苛模式(StrictMode)甚至能在從一個對象到另一個對象的控制流中找到違例事件。當違例發生,你會驚奇地注意到代碼正運行於主線程,而棧trace將幫助你發現它如何發生。於是你能單步調試解決問題,或是將代碼移到它自己的後臺線程,或是就保持原來的處理方式。這都取決與你。當然,你可能希望適時關閉嚴苛模式(StrictMode),當你的程序作爲產品發佈時,你可不希望它僅爲了一個警告在你的用戶手裏崩潰。
有兩個方法可以關閉嚴苛模式(StrictMode),最直接的就是移除相應代碼,但這樣做不利於持續開發的產品。你通常可以定義一個應用級別布爾變量來測試是否需要調用嚴苛模式(StrictMode)代碼。在發佈產品前將這個值定義爲FALSE。更優雅的方式是利用調試模式(debug mode)的特點,在AndroidManifest.xml中定義這個布爾變量。<application>字段的屬性之一是android:debuggable,其義自明。列表2-11給出了利用該特性的控釋方法。
列表2-11 僅在調試模式設置嚴苛模式(StrictMode)
- // Return if this application is not in debug mode
- ApplicationInfo appInfo = context.getApplicationInfo();
- int appFlags = appInfo.flags;
- if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- // Do StrictMode setup here
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDeath()
- .build());
- }
嚴苛模式(StrictMode)很不錯,不過在Android 2.3之前的版本上該模式不工作。爲了避免這個問題,你要在StrictMode對象還不存在的時候就驗證版本是否在Android2.3及以上。你能利用反射技術(reflection),當嚴苛模式(StrictMode)函數有效時間接調用它,反之不去調用。方法很簡單,你能按列表2-12中的代碼處理
列表2-12 利用反射技術(reflection)調用嚴苛模式(StrictMode)
- try {
- Class sMode = Class.forName("android.os.StrictMode");
- Method enableDefaults = sMode.getMethod("enableDefaults");
- enableDefaults.invoke(null);
- }
- catch(Exception e) {
- // StrictMode not supported on this device, punt
- Log.v("StrictMode", "... not supported. Skipping...");
- }
某些時候你不希望報告所有違例。那在主線程之外的其他線程中設置嚴苛模式(StrictMode)很不錯。譬如,你需要在正在監視的線程中進行磁盤讀取。此時,你要麼不去調用detectDiskReads(),要麼在調用detectAll()之後跟一個permitDiskReads()。類似允許函數也適用於其他操作。但要是你要在Anroid2.3之前版本上做這些事,有辦法嗎?當然有。
當應用中嚴苛模式(StrictMode)無效,如果你試圖訪問它,將拋出一個VerifyError異常。如果你將嚴苛模式(StrictMode)封裝在一個類裏,並捕捉這個錯誤,當嚴苛模式(StrictMode)無效時,你能忽略它。列表2-13顯示一個簡單的嚴苛模式(StrictMode)封裝類StrictModeWrapper。列表2-14顯示瞭如何在你的應用中使用這個封裝類。
列表 2–13 在Anroid2.3之前版本建立嚴苛模式(StrictMode)封裝類
- import android.content.Context;
- import android.content.pm.ApplicationInfo;
- import android.os.StrictMode;
- public class StrictModeWrapper {
- public static void init(Context context) {
- // check if android:debuggable is set to true
- int appFlags = context.getApplicationInfo().flags;
- if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskReads()
- .detectDiskWrites()
- .detectNetwork()
- .penaltyLog()
- .build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDeath()
- .build());
- }
- }
- }
列表 2–14 在Anroid2.3之前版本調用嚴苛模式(StrictMode)封裝類
- try {
- StrictModeWrapper.init(this);
- }
- catch(Throwable throwable) {
- Log.v("StrictMode", "... is not available. Punting...");
- }
//如果考慮到關於版本兼容問題,因爲按照上面的寫法在2.3以下系統是沒有問題的,但是在2.3以上的話,就會出錯,所以應該採用以下方式來處理:
- @SuppressLint("NewApi")
- public static void init(Context context) {
- // check if android:debuggable is set to true
- int appFlags = context.getApplicationInfo().flags;
- if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- try {
- //Android 2.3及以上調用嚴苛模式
- Class sMode = Class.forName("android.os.StrictMode");
- Method enableDefaults = sMode.getMethod("enableDefaults");
- enableDefaults.invoke(null);
- } catch (Exception e) {
- // StrictMode not supported on this device, punt
- Log.v("StrictMode", "... not supported. Skipping...");
- }
- /*
- * StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- * .detectDiskReads() .detectDiskWrites() .detectNetwork()
- * .penaltyLog() .build()); StrictMode.setVmPolicy(new
- * StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects()
- * .penaltyLog() .penaltyDeath() .build());
- */
- }
- }