各位戰士,歡迎來到第二十六天的戰場。至今,我們已經學習了無數的軍規鐵律:不要阻塞主執行緒、使用協程處理 I/O、用 WorkManager 執行後台任務……。但理論和實踐之間,總有差距。在複雜的專案和緊迫的時程中,我們,甚至是經驗豐富的老兵,都可能偶然寫下違反紀律的程式碼。
我們需要一個自動化的「糾察隊」,一個在我們犯錯時會立刻大聲喝止的「魔鬼教官」。這個教官,就是 Android SDK 內建的開發者工具——StrictMode
(嚴格模式)。
StrictMode
是一個專為開發階段設計的監控工具。當你在應用程式中啟用它時,它會像一個哨兵一樣,監視你的程式碼行為。一旦發現你做了某些「壞事」(例如在主執行緒上讀寫檔案),它就會立刻以你指定的方式發出警告。
最高指令:StrictMode
絕對、絕對不能在 Release 發佈版本中啟用! 它僅用於在 Debug 版本中幫助我們發現潛在問題。
StrictMode
能監控什麼?StrictMode
主要負責執行兩套「軍法」:
執行緒策略 (ThreadPolicy):這是我們最關心的。它專門監視主執行緒上的違規行為,是我們抓 Jank 的好幫手。主要監控的行為包括:
detectDiskReads()
)
detectDiskWrites()
)
detectNetwork()
)
虛擬機策略 (VmPolicy):監視與記憶體和資源洩漏相關的違規行為。主要監控的行為包括:
detectLeakedSqlLiteObjects()
)
Closeable
物件洩漏(如 Cursor
, FileStream
等)detectActivityLeaks()
)
啟用 StrictMode
的最佳時機,是在你自訂的 Application
類別的 onCreate()
方法中。這樣可以確保它在應用程式一啟動時就開始監視。
關鍵步驟:
import android.app.Application
import android.os.StrictMode
import com.your.package.name.BuildConfig // 確保 import 了你專案的 BuildConfig
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 核心原則:只在 Debug 版本中啟用 StrictMode
if (BuildConfig.DEBUG) {
setupStrictMode()
}
// ... 其他的應用程式初始化 ...
}
private fun setupStrictMode() {
// 設定「執行緒策略」
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads() // 監控磁碟讀取
.detectDiskWrites() // 監控磁碟寫入
.detectNetwork() // 監控網路請求
.penaltyLog() // 偵測到違規時,在 Logcat 中印出詳細堆疊
// .penaltyDeath() // (可選) 偵測到違規時,直接讓 App 崩潰 (最嚴格)
.build()
)
// 設定「虛擬機策略」
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects() // 監控 SQLite 物件洩漏
.detectLeakedClosableObjects() // 監控 Closeable 物件洩漏
.penaltyLog() // 同樣在 Logcat 中印出
.build()
)
}
}
if (BuildConfig.DEBUG)
:這是啟用 StrictMode
的金標準,確保這段程式碼在 Release 版本中會被完全移除。
.detect...()
:指定要監控哪些違規行為。
.penalty...()
:指定偵測到違規行為後的「懲罰措施」。
penaltyLog()
:最常用也是最推薦的。它不會干擾你正常測試,只會在 Logcat 中用 StrictMode
這個 Tag 印出詳細的錯誤報告,包含完整的函式呼叫堆疊,讓你精準定位到出問題的那一行程式碼。
penaltyDeath()
:最嚴格的懲罰,一旦違規,App 立刻崩潰。適合在進行專項整治,決心要修復某個問題時短暫開啟。
當你啟用了 penaltyLog() 之後,一旦發生違規,你就會在 Logcat 中看到類似這樣的紅色日誌:
E/StrictMode: StrictMode policy violation; ~duration=23ms: android.os.strictmode.DiskReadViolation
at android.os.StrictMode$AndroidBlockGuardPolicy.onDiskRead(StrictMode.java:1593)
at java.io.FileInputStream.<init>(FileInputStream.java:151)
at com.example.myapp.MyRepository.loadDataFromFile(MyRepository.java:42)
at com.example.myapp.MyViewModel.loadData(MyViewModel.java:25)
at com.example.myapp.MainActivity.onCreate(MainActivity.java:30)
...
這份報告提供了所有你需要的情報:
違規類型:DiskReadViolation
(磁碟讀取違規)。
大概耗時:~duration=23ms
(這次違規大概花了 23 毫秒,已經超過 16ms 了!)。
違規路徑:完整的堆疊追蹤,告訴你問題發生在 MyRepository.java
的第 42 行,而它是在 MainActivity
的 onCreate
中被呼叫的。
有了這份報告,你就等於拿到了一張直達問題根源的地圖。
今天,我們為我們的開發流程建立了一套自動化的紀律監督系統。
我們認識了 StrictMode
這個只應在開發階段使用的強大「哨兵」。
我們學會了如何設定 ThreadPolicy
來監控主執行緒的 I/O 操作,以及如何設定 VmPolicy
來監控資源洩漏。
我們知道了如何閱讀 StrictMode
的日誌,並根據堆疊追蹤快速定位問題程式碼。
StrictMode
像一位嚴厲但有益的教官,它能幫助我們在開發早期就根除壞習慣,建立起對性能問題的肌肉記憶。
我們已經學會了如何在開發時「嚴于律己」。但隨著專案演進,我們要如何確保今天優異的性能不會在未來被某次不經意的程式碼提交給破壞掉?明天,我們將學習建立一個自動化的「靶場」,使用 Jetpack Macrobenchmark 來為我們的應用性能建立一個可量化的基準,並持續追蹤。
我們明天見!