做用:WorkManager
負責用來管理後臺任務。java
區別: 它和一個異步任務以及 Service
有什麼區別呢?看完你就知道了android
相關類:網絡
Worker
:任務的執行者,是一個抽象類,須要繼承它實現要執行的任務。異步
WorkRequest
:指定讓哪一個 Woker
執行任務,指定執行的環境,執行的順序等。要使用它的子類 OneTimeWorkRequest
或 PeriodicWorkRequest
。ide
WorkManager
:管理任務請求和任務隊列,發起的 WorkRequest
會進入它的任務隊列。模塊化
WorkStatus
:包含有任務的狀態和任務的信息,以 LiveData
的形式提供給觀察者。gradle
build.gradle
中配置:ui
implementation "android.arch.work:work-runtime:$work_version" implementation "android.arch.work:work-firebase:$work_version"
gradle.properties
中配置:this
work_version=1.0.0-alpha10
第一步:定義 worker
線程
咱們定義 MainWorker
繼承 Worker
,發現須要重寫 doWork
方法,而且須要返回任務的狀態 WorkerResult
:
class MainWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result { // todo 要執行的任務 return Result.SUCCESS } }
暫時什麼都不作,直接返回任務執行完成 Result.SUCCESS
。
第二步:定義 WorkRequest
在 MainActivity
中定義 WorkRequest
:
val request =OneTimeWorkRequest.Builder(MainWork::class.java) .build()
OneTimeWorkRequest
意味着這個任務只需執行一遍。
第三步:加入任務隊列
要讓任務執行,須要將 WorkRequest
加入任務隊列:
WorkManager.getInstance().enqueue(request)
如今加入任務隊列後,任務會立刻獲得執行。 但須要注意的是,這句代碼的做用是將任務加入任務隊列,而不是執行任務。由於任務可能須要等到知足環境條件的狀況纔會執行。
後臺任務少不了數據的交互,咱們看一下數據是如何傳入傳出的。
2.一、input
Data
對象:Data
的使用和 Bundle
差很少。val dateFormat = SimpleDateFormat("hh:mm:ss", Locale.getDefault()) // Data 對象 val data = Data.Builder() .putString("time",dateFormat.format(Date())) .build()
WorkRequest
的 setInputData
方法傳遞 Data
:val request = OneTimeWorkRequest.Builder(MainWork::class.java) .setInputData(data) // 傳遞data .build()
Worker
中,從 inputData
能夠取到數據:class MainWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result { // 獲取傳遞的數據,inputdata 實際上是 getInputData() val time = inputData.getString("time") return Result.SUCCESS } }
2.二、output
當任務處理完了,須要將處理結果返回。
Data
並賦值給 outputData
:class MainWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result { val time = inputData.getString("time") // 傳出 outputData = Data.Builder() .putString("name","Hello Worker!") .build() return Result.SUCCESS } }
Worker
傳遞出來的數據每個 WorkRequest
都會有一個 id
,經過 id
能夠獲取到對應任務的 WorkStatus
,而且是以 LiveData
形式提供的:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dateFormat = SimpleDateFormat("hh:mm:ss", Locale.getDefault()) val data = Data.Builder().putString("time", dateFormat.format(Date())).build() val request = OneTimeWorkRequest.Builder(MainWork::class.java) .setInputData(data) .build() WorkManager.getInstance().enqueue(request) WorkManager.getInstance() // 經過 id .getStatusByIdLiveData(request.id) .observe(this, Observer { if (it != null && it.state.isFinished) { // 取出 outputData val name = it.outputData.getString("name") Log.e("eeee==>>",name) } }) } }
若是須要取消一個在隊列中的任務,也是經過 id
實現的:
WorkManager.getInstance().cancelWorkById(request.id)
WorkRequest
詳解WorkManager
容許咱們指定任務執行的環境,好比網絡已鏈接、電量充足時等,在知足條件的狀況下任務纔會執行。
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) // 網絡狀態 .setRequiresBatteryNotLow(true) // 不在電量不足時執行 .setRequiresCharging(true) // 在充電時執行 .setRequiresStorageNotLow(true) // 不在存儲容量不足時執行 .setRequiresDeviceIdle(true) // 在待機狀態下執行,須要 API 23 .build() val request = OneTimeWorkRequest. Builder(MainWorker::class.java) .setConstraints(constraints) .build()
網絡狀態可選值以下:
狀態 | 說明 |
---|---|
NOT_REQUIRED |
沒有要求 |
CONNECTED |
網絡鏈接 |
UNMETERED |
鏈接無限流量的網絡 |
METERED |
鏈接按流量計費的網絡 |
NOT_ROAMING |
鏈接非漫遊網絡 |
舉例:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dateFormat = SimpleDateFormat("hh:mm:ss", Locale.getDefault()) val data = Data.Builder().putString("time", dateFormat.format(Date())).build() val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val request = OneTimeWorkRequest.Builder(MainWork::class.java) // 添加約束條件 .setConstraints(constraints) .setInputData(data) .build() WorkManager.getInstance().enqueue(request) WorkManager.getInstance() // 經過 id .getStatusByIdLiveData(request.id) .observe(this, Observer { if (it != null && it.state.isFinished) { val name = it.outputData.getString("name") Log.e("eeee==>>", name) } }) } }
觀察,當有網絡的時候,纔會打印 E/eeee==>>: Hello Worker!
OneTimeWorkRequest
任務只須要執行一遍
PeriodicWorkRequest
能夠發起一個屢次執行的定時任務。
如:
val request = PeriodicWorkRequest .Builder(MainWorker::class.java, 15, TimeUnit.MINUTES) .setConstraints(constraints) .setInputData(data) .build()
這樣,發起的任務就會每隔 15
分鐘執行一次。
除了須要傳入間隔時間,使用起來跟 OneTimeWorkRequest
是沒有區別的。
你可能會想更頻繁的去執行一個任務,好比幾秒鐘執行一遍,但很遺憾,最小時間間隔就是 15
分鐘,看一下源碼就知道了。
還有須要注意的是,定時任務並非說通過指定時間後它就立刻執行,而是通過這一段時間後,等到知足約束條件等狀況時,它才執行。
先看代碼:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dateFormat = SimpleDateFormat("hh:mm:ss", Locale.getDefault()) val data = Data.Builder().putString("time", dateFormat.format(Date())).build() val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val request = OneTimeWorkRequest.Builder(MainWork::class.java) // 添加約束條件 .setConstraints(constraints) .setInputData(data) .build() WorkManager.getInstance().enqueue(request) WorkManager.getInstance() // 經過 id .getStatusByIdLiveData(request.id) .observe(this, Observer { if (it != null && it.state.isFinished) { val name = it.outputData.getString("name") Log.e("eeee==>>", name) } }) } }
操做步驟: 斷網 -> 將進程殺掉 ->聯網 ->再次運行
不出意外的話,這時候你會看到有兩個時間的打印,並且兩個時間還不同,這是爲何呢?
這說明了,就算進程被殺掉,任務仍是存在,甚至若是重啓手機,任務依然會在知足條件的狀況下獲得執行。
這是 WorkManager
的另外一個特色:
一旦發起一個任務,任務是能夠保證必定會被執行的,就算退出應用,甚至重啓手機都阻止不了他。但可能因爲添加了環境約束等緣由,它執行的時間是不肯定的。
當應用正在運行時,它會在當前的進程中啓用一個子線程執行。應用沒有運行的狀況下啓用,它則會本身選擇一種合適的方式在後臺運行。具體是什麼方式和 Android
的版本和依賴環境有關:
WorkManager
容許咱們按照必定的順序執行任務,好比我想 A
、B
、C
三個任務按前後順序執行:
能夠這樣寫,把它們組成一條任務鏈:
WorkManager.getInstance() .beginWith(workA) .then(workB) .then(workC) .enqueue()
這樣的話,上一個任務的 outputData
會成爲下一個任務的 inputData
。
再更復雜一點,我想 A
和 B
同時執行,它們都執行完以後,再執行 C
:
也是能夠實現的:
WorkManager.getInstance() .beginWith(workA,workB) .then(workC) .enqueue()
再更更復雜一點,若是我想這樣:
這樣就須要先把 A、B 和 C、D 分別組成一條任務鏈,再進行聯結:
val chain1 = WorkManager.getInstance() .beginWith(workA) .then(workB) val chain2 = WorkManager.getInstance() .beginWith(workC) .then(workD) val chain3 = WorkContinuation .combine(chain1, chain2) .then(workE) chain3.enqueue()
再更更更復雜一點,若是我把定時任務放進去會怎樣? 很差意思,鏈式任務只支持 OneTimeWorkRequest
。
使用任務鏈,咱們能夠將各類任務進行模塊化。一樣的,任務鏈不保證每一個任務執行的時間,可是保證它們執行的前後順序。
任務惟一性:
不少狀況下,咱們但願在任務隊列裏,同一個任務只存在一個,避免任務的重複執行,這時候能夠用到 beginUniqueWork
這個方法:
WorkManager.getInstance() .beginUniqueWork("unique", ExistingWorkPolicy.REPLACE, request) .enqueue()
須要傳入一個任務的標籤,和重複任務的執行方式,可取值以下:
狀態 | 說明 |
---|---|
REPLACE | 刪除已有的任務,添加現有的任務 |
KEEP | 什麼都不作,不添加新任務,讓已有的繼續執行 |
APPEND | 加入已有任務的任務鏈最末端。追加,舊任務執行以後再執行新的任務。 |
但這種方式也是隻支持 OneTimeWorkRequest
。若是是 PeriodicWorkRequest
,我想到的辦法是每次執行以前,根據標籤去取消已有的任務。