StrictMode 是 Android 開發中用於檢測應用程式主線程違規行為的強大工具。它能即時捕捉磁碟 I/O、網路存取等耗時操作在主線程執行的情況,並提供詳細的堆疊追蹤幫助開發者快速定位問題。除了主線程,StrictMode 同樣支援在 Handler、AsyncTask、AsyncQueryHandler、IntentService 等非同步元件中使用。
StrictMode 提供兩大類檢查策略:
當監測到策略違規時,系統會根據配置採取不同懲罰措施:輸出至 LogCat、顯示對話框、閃爍螢幕、寫入 DropBox 日誌,或直接讓應用崩潰。開發階段最常用的是 penaltyLog() 輸出日誌或 penaltyDeath() 讓應用崩潰以強制修復。
public void onCreate() {
if (DEVELOPER_MODE) {
// 線程策略:檢測磁碟讀寫、網路存取,違規時記錄日誌並顯示對話框
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyDialog()
.penaltyLog()
.build());
// 虛擬機策略:檢測 SQLite 與 Closeable 物件洩漏,違規時記錄日誌並崩潰
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
使用 detectAll() 可一次啟用所有可檢測項目。正式發布時必須移除或禁用 StrictMode,避免使用者遇到應用崩潰。推薦做法是利用 AndroidManifest.xml 的 android:debuggable 屬性動態控制:
<application android:debuggable="true" ... >
ApplicationInfo appInfo = context.getApplicationInfo();
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// 僅在除錯模式啟用 StrictMode
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
以下程式碼在主線程執行網路請求,必定觸發 StrictMode 網路違規檢測:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyDialog()
.penaltyLog()
.build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 主線程直接發起網路請求(錯誤示範)
HttpGet httpGet = new HttpGet("https://www.baidu.com");
HttpClient httpClient = new DefaultHttpClient();
// ... 執行請求並處理回應
}
}
LogCat 中搜尋 StrictMode policy violation 可見詳細堆疊追蹤:
D/StrictMode(15454): StrictMode policy violation; ~duration=461 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=55 violation=4 D/StrictMode(15454): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1153) D/StrictMode(15454): at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249) ... D/StrictMode(15454): at com.imooc.strictmodetest.MainActivity.onCreate(MainActivity.java:53)
若使用 penaltyDropbox(),可透過 ADB 指令查看 DropBox 日誌:
adb shell dumpsys dropbox data_app_strictmode --print
某些情況下(如資料庫初始化)必須在主線程寫入磁碟,可使用 permitDiskWrites() 暫時放寬限制:
StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(old)
.permitDiskWrites()
.build());
// 執行必要的磁碟寫入操作
StrictMode.setThreadPolicy(old); // 恢復原策略