如果我們在主執行緒(Main thread、UI thread)上執行耗時的工作,例如檔案讀寫、資料庫操作、網路操作等,UI 就會沒有反應,甚至產生 ANR。解決的方式是簡單的使用 Coroutine 將耗時的任務在背景執行。
lifecycleScope.launch {
//跟後端取得資料
val data = networkCall()
//回到UI執行緒
binding.result.text = data
}
private suspend fun networkCall(): String {
return withContext(Dispatchers.IO) {
//在Background thread執行
//模擬2秒後取得資料
delay(2000)
"Data1"
}
}
只是將耗時工作放到背景 (Background thread) 處理,對於某些情境仍是不夠的。這種在背景執行的工作,預期使用者會在你的App中等待工作完成。一但遇到執行的工作需要更多的時間,使用者就會離開 App,而這個在背景執行的工作,並不能保證一定會執行完成。
我們把會在app執行的背景任務,區分為這幾種來看:
需要立即執行的背景任務,例如檔案讀寫、資料庫操作、網路操作等等,這幾種背景任務的執行一般都是做完就結束了,所以使用 Background thread 即可。
當任務的執行需要更長的時間,使用者就可能離開 App。因此需要一個在 App離開時仍可以在背景處理的WorkManager。例如你在Line要傳送影片給朋友,傳送影片並非馬上就能完成,使用者也可能先離開 App。透過WorkManager 就可以達到使用者離開App時仍在背景傳送影片。
有一種背景工作是需要持續性的執行,例如音樂撥放、語音通話、視訊通話。這種就需要使用 Foreground service。Foreground service 可以在你離開App時持續的在背景工作,直到我們停止這個工作。也因為這是持續性的,較耗效能,所以會在通知列顯示這個工作正在執行,例如你在kkbox撥放音樂,就會在通知例看到音樂正在撥放。
有些工作不需要在當下立即執行,就可以透過 WorkManager來延後執行。而這個工作需要在離開app後仍被執行,且執行的結果不用馬上讓使用者知道。例如你搜集了使用者使用App的資訊,將這些數據發送至後端伺服器。這就是一個可以延後再執行的工作。
當任務為週期性時,而且要在離開App時仍可以執行,就可以使用 PeriodicWorkRequest 來執行週期性的任務,這個週期性任務最短週期為15分鐘一次。也避免你自已寫一個在背景執行的計時器造成資源浪費。例如每隔半小時需要與後端同步一次資訊就很適合使用 PeriodicWorkRequest。
如果這個任務只需要在特定的時間執行,便使用 Alarm 來達成。即使你的 App 已經不在前景執行,或是裝置已經進到閒置狀態,仍可以在指定的時間執行指定的工作。你不要自已設定一個計時器,或一直在背景執行。
總結一下這幾種工作的類型:
選擇適當的服務來執行不同情境的工作,就是希望你不要一直佔用資源。只在需要的時候執行工作,而不是在背景持續的執行。當 App 在前景時,基本上要做什麼都可以,一但裝置進到閒置狀況,Android 就會限制你的使用行為,畢竟使用者不會接受沒在用手機時候,手機卻依然是耗電的。
參考:
https://developer.android.com/guide/background
https://developer.android.com/training/scheduling/alarms
https://developer.android.com/topic/libraries/architecture/workmanager/basics