當 App 在主執行緒長時間阻塞就會發生 ANR。如果這個阻塞的只發生了很短的 1 秒,雖然不會 ANR,但其實也不是使用者可以接受的。正常的 UI 回應速度應該在 0.1~0.2秒之間。這篇就要來介紹使用 StrictMode 來幫助我們找到這些在主執行緒的異常操作。
StrictMode 可以為你偵測是否在主執行緒執行了網路請求或檔案存取。使用 StrictMode 的方式是在 Activity
或 Application
的 onCreate
加入下方程式碼來啟動 StrictMode。
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //偵測檔案讀取
.detectDiskWrites() //偵測檔案寫入
.detectNetwork() //偵測網路請求
.penaltyFlashScreen() //違規的反應方式
.build()
)
StrictMode 的運作機制為:
你可以分別設定需要偵測什麼樣的異常行為:
detectDiskReads() 偵測檔案讀取
detectDiskWrites() 偵測檔案寫入
detectNetwork() 偵測網路請求
detectAll() 偵測所有的異常行為
在 StrictMode 偵測到違反規則時,可以選擇以下幾個反應方式:
penaltyLog(): 寫入Log
penaltyDeath(): 讓App閃退
penaltyDialog(): 顯示Dialog
penaltyDeathOnNetwork(): 如果是網路請求就讓閃退
penaltyFlashScreen(): 螢幕會閃一下
我們來實際寫一個在主執行緒存取檔案的例子,透過 SharedPreferences 來寫入一筆資料。
binding.saveData.setOnClickListener {
//直在接主執行緒寫入資料
saveData()
}
private fun saveData() {
val pref = getSharedPreferences("StrictModeSample", MODE_PRIVATE)
pref.edit()
.putString("data", "data")
.apply()
}
這段程式碼直接執行就會因為 detectDiskWrites
的設定而被 StrictMode 判定為異常。接著如果通知方式設定為 penaltyLog
,則會顯示 log 如下:
StrictMode policy violation; ~duration=50 ms: android.os.strictmode.DiskWriteViolation
通知方式設定為 penaltyDialog
則會彈出警告視窗。
通知方式設定為 penaltyFlashScreen 則會讓畫面閃一下紅框。
最後,StrictMode 並不是一種安全機制,不能保證找到所有的網路請求、磁碟存取。例如在 JNI 的網路請求或磁碟存取就不一定會觸發。它的主要功能是讓你找出潛在的問題。但也不是每個 StrictMode 找出的問題都要修復它,例如這裡用的範例 sharedpreference
是以一對 Key 與 Value 的方式來儲存資料,雖然也是檔案儲存違反了StrictMode的規定,但實際上它的存取速度是很快的。一般為了方便我們還是會在主執行緒來直接執行。最後,也請記得只在 debug 模式開啟 StrictMode ,不要在 release 模式開啟,以免影響使用者正常操作。
參考:
https://developer.android.com/reference/android/os/StrictMode