เมื่อกำหนดWorker
และWorkRequest
แล้ว
ขั้นตอนสุดท้ายคือการจัดคิวงาน วิธีที่ง่ายที่สุดในการจัดคิวงาน
คือการเรียกใช้เมธอด enqueue()
ของ WorkManager โดยส่ง WorkRequest
ที่คุณ
ต้องการเรียกใช้
Kotlin
val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)
Java
WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);
โปรดระมัดระวังเมื่อจัดคิวงานเพื่อหลีกเลี่ยงการทำซ้ำ เช่น แอปอาจพยายามอัปโหลด บันทึกไปยังบริการแบ็กเอนด์ทุก 24 ชั่วโมง หากไม่ระมัดระวัง คุณอาจ ลงเอยด้วยการจัดคิวงานเดียวกันหลายครั้ง แม้ว่างานจะต้องการ เรียกใช้เพียงครั้งเดียวก็ตาม หากต้องการบรรลุเป้าหมายนี้ คุณสามารถกำหนดเวลางานเป็นงานที่ไม่ซ้ำกันได้
ผลงานที่ไม่ซ้ำ
งานที่ไม่ซ้ำกันเป็นแนวคิดที่มีประสิทธิภาพซึ่งรับประกันว่าคุณจะมีอินสแตนซ์ของงานที่มีชื่อหนึ่งๆ เพียงอินสแตนซ์เดียวในแต่ละครั้ง ชื่อที่ไม่ซ้ำกันต่างจากรหัสตรงที่มนุษย์อ่านได้และนักพัฒนาแอปเป็นผู้ระบุแทนที่ WorkManager จะสร้างขึ้นโดยอัตโนมัติ ชื่อที่ไม่ซ้ำจะเชื่อมโยงกับผลงานเพียงรายการเดียวเท่านั้น ซึ่งต่างจากแท็ก
งานที่ไม่ซ้ำกันสามารถนำไปใช้กับงานแบบครั้งเดียวและงานเป็นระยะได้ คุณสร้างลำดับงานที่ไม่ซ้ำกันได้โดยเรียกใช้เมธอดใดเมธอดหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับว่าคุณกำลังกำหนดเวลาให้งานเกิดซ้ำหรืองานแบบครั้งเดียว
WorkManager.enqueueUniqueWork()
สำหรับงานแบบครั้งเดียวWorkManager.enqueueUniquePeriodicWork()
สำหรับงานเป็นระยะๆ
ทั้ง 2 วิธีนี้ยอมรับอาร์กิวเมนต์ 3 รายการ
- uniqueWorkName -
String
ที่ใช้เพื่อระบุคำของานที่ไม่ซ้ำกัน - existingWorkPolicy -
enum
ซึ่งบอก WorkManager ว่าต้องทำอะไร หากมีงานที่ยังไม่เสร็จซึ่งเชื่อมโยงกันอยู่แล้วและมีชื่อที่ไม่ซ้ำกัน ดูข้อมูลเพิ่มเติมได้ในนโยบายการแก้ปัญหาความขัดแย้ง - work -
WorkRequest
ที่จะกำหนดเวลา
การใช้ผลงานที่ไม่ซ้ำกันจะช่วยให้เราแก้ไขปัญหาการกำหนดเวลาที่ซ้ำกันซึ่งได้กล่าวถึงไปก่อนหน้านี้ได้
Kotlin
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
Java
PeriodicWorkRequest sendLogsWorkRequest = new
PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
.setConstraints(new Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest);
ตอนนี้หากโค้ดทำงานขณะที่งาน sendLogs อยู่ในคิวแล้ว ระบบจะเก็บงานที่มีอยู่และจะไม่เพิ่มงานใหม่
ลำดับงานที่ไม่ซ้ำกันยังมีประโยชน์ในกรณีที่คุณต้องสร้างเชนของงานที่ยาวขึ้นเรื่อยๆ เช่น แอปแต่งภาพอาจอนุญาตให้ผู้ใช้ยกเลิก การดำเนินการที่ต่อเนื่องกัน การเลิกทำแต่ละรายการอาจใช้เวลาสักครู่ แต่ ต้องดำเนินการตามลำดับที่ถูกต้อง ในกรณีนี้ แอปอาจสร้างเชน "เลิกทำ" และต่อท้ายการดำเนินการเลิกทำแต่ละรายการในเชนตามต้องการ ดูรายละเอียดเพิ่มเติมได้ที่การเชื่อมโยงงาน
นโยบายการแก้ไขข้อขัดแย้ง
เมื่อกำหนดเวลาสำหรับงานที่ไม่ซ้ำ คุณต้องบอก WorkManager ว่าจะให้ดำเนินการใดเมื่อเกิดความขัดแย้ง โดยทำได้ด้วยการส่งผ่าน Enum เมื่อจัดคิวงาน
สำหรับงานแบบครั้งเดียว คุณต้องระบุ
ExistingWorkPolicy
ซึ่ง
รองรับตัวเลือก 4 รายการสำหรับการจัดการข้อขัดแย้ง
REPLACE
ที่มีอยู่กับงานใหม่ ตัวเลือกนี้จะยกเลิกงานที่มีอยู่KEEP
งานที่มีอยู่และ ไม่สนใจงานใหม่APPEND
งานใหม่ต่อท้ายงานที่มีอยู่ นโยบายนี้จะทำให้งานใหม่ของคุณเชื่อมโยงกับงานที่มีอยู่ โดยจะทำงานหลังจากงานที่มีอยู่เสร็จสิ้น
งานที่มีอยู่จะกลายเป็นข้อกำหนดเบื้องต้นสำหรับงานใหม่ หากงานที่มีอยู่กลายเป็น CANCELLED
หรือ FAILED
งานใหม่ก็จะเป็น CANCELLED
หรือ FAILED
ด้วย
หากต้องการให้งานใหม่ทำงานโดยไม่คำนึงถึงสถานะของงานที่มีอยู่
ให้ใช้ APPEND_OR_REPLACE
แทน
APPEND_OR_REPLACE
ทํางานคล้ายกับAPPEND
ยกเว้นว่าไม่ได้ขึ้นอยู่กับสถานะงานข้อกำหนดเบื้องต้น หากงานที่มีอยู่เป็นCANCELLED
หรือFAILED
งานใหม่จะยังคงทำงานได้
สำหรับงานแบบช่วงเวลา คุณต้องระบุ
ExistingPeriodicWorkPolicy
ซึ่งรองรับ 2 ตัวเลือก ได้แก่ REPLACE
และ KEEP
ตัวเลือกเหล่านี้ทํางานเหมือนกับ
คู่ของ ExistingWorkPolicy
การสังเกตงานของคุณ
หลังจากจัดคิวงานแล้ว คุณจะตรวจสอบสถานะได้ทุกเมื่อโดยการค้นหา WorkManager ตาม name
id
หรือตาม tag
ที่เชื่อมโยงกับงานนั้น
Kotlin
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
Java
// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
การค้นหาจะแสดงListenableFuture
ของออบเจ็กต์ WorkInfo
ซึ่งรวมถึงid
ของงาน แท็กของงาน State
ปัจจุบัน และชุดข้อมูลเอาต์พุตใดๆ ที่ใช้ Result.success(outputData)
รูปแบบ LiveData
และ Flow
ของแต่ละเมธอดช่วยให้คุณสังเกตการเปลี่ยนแปลงของ
WorkInfo
ได้โดยการลงทะเบียน Listener เช่น หากต้องการแสดงข้อความแก่ผู้ใช้เมื่อการดำเนินการบางอย่างเสร็จสมบูรณ์ คุณสามารถตั้งค่าได้ดังนี้
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect{ workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
Java
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo.getState() != null &&
workInfo.getState() == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show();
}
});
คำค้นหาที่ซับซ้อนเกี่ยวกับงาน
WorkManager 2.4.0 ขึ้นไปรองรับการค้นหาที่ซับซ้อนสำหรับงานที่อยู่ในคิวโดยใช้ออบเจ็กต์
WorkQuery
WorkQuery รองรับ
การค้นหางานโดยใช้ชุดค่าผสมของแท็ก สถานะ และชื่องานที่ไม่ซ้ำกัน
ตัวอย่างต่อไปนี้แสดงวิธีค้นหางานทั้งหมดที่มีแท็ก "syncTag"
ซึ่งอยู่ในสถานะ FAILED
หรือ CANCELLED
และมีชื่อที่ไม่ซ้ำกันเป็น "preProcess" หรือ "sync"
Kotlin
val workQuery = WorkQuery.Builder
.fromTags(listOf("syncTag"))
.addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(listOf("preProcess", "sync")
)
.build()
val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)
Java
WorkQuery workQuery = WorkQuery.Builder
.fromTags(Arrays.asList("syncTag"))
.addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(Arrays.asList("preProcess", "sync")
)
.build();
ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);
คอมโพเนนต์แต่ละรายการ (แท็ก สถานะ หรือชื่อ) ใน WorkQuery
จะAND
ร่วมกับคอมโพเนนต์อื่นๆ ค่าแต่ละค่าในคอมโพเนนต์จะOR
-ed เช่น (name1 OR name2
OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...)
WorkQuery
ยังใช้ได้กับ LiveData ที่เทียบเท่า
getWorkInfosLiveData()
และ Flow ที่เทียบเท่า getWorkInfosFlow()
การยกเลิกและหยุดงาน
หากไม่ต้องการให้งานที่เข้าคิวไว้ก่อนหน้านี้ทำงานอีกต่อไป คุณสามารถขอให้ระบบยกเลิกงานนั้นได้ name
id
หรือtag
ที่เชื่อมโยงกับงานสามารถยกเลิกงานได้
Kotlin
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
Java
// by id
workManager.cancelWorkById(syncWorker.id);
// by name
workManager.cancelUniqueWork("sync");
// by tag
workManager.cancelAllWorkByTag("syncTag");
เบื้องหลัง WorkManager จะตรวจสอบState
ของงาน หากงานเสร็จแล้ว
ระบบจะไม่ดำเนินการใดๆ มิเช่นนั้น สถานะของงานจะเปลี่ยนเป็น
CANCELLED
และงาน
จะไม่ทำงานในอนาคต งานใดๆ ที่WorkRequest
ขึ้นอยู่กับงานนี้จะCANCELLED
ด้วย
RUNNING
ทำงาน
รับสายถึง
ListenableWorker.onStopped()
ลบล้างเมธอดนี้เพื่อจัดการการล้างข้อมูลที่อาจเกิดขึ้น ดูข้อมูลเพิ่มเติมได้ที่หยุด
Worker ที่ทำงานอยู่
หยุด Worker ที่ทำงานอยู่
WorkManager อาจหยุดWorker
ที่กำลังทำงานเนื่องจากสาเหตุบางประการ ดังนี้
- คุณขอให้ยกเลิกอย่างชัดเจน (เช่น โดยการโทรหา
WorkManager.cancelWorkById(UUID)
) - ในกรณีของงานที่ไม่ซ้ำกัน
คุณได้จัดคิว
WorkRequest
ใหม่โดยชัดแจ้งด้วยExistingWorkPolicy
ของREPLACE
ระบบจะถือว่าWorkRequest
เก่าถูกยกเลิกทันที - ไม่เป็นไปตามข้อจำกัดของงานอีกต่อไป
- ระบบสั่งให้แอปหยุดการทำงานด้วยเหตุผลบางอย่าง ปัญหานี้อาจเกิดขึ้นหากคุณใช้เวลาดำเนินการเกินกำหนดเวลา 10 นาที ระบบ กำหนดเวลาให้ลองอีกครั้งในภายหลัง
ระบบจะหยุด Worker ของคุณในกรณีต่อไปนี้
คุณควรยกเลิกงานที่กำลังดำเนินการอยู่และปล่อยทรัพยากรที่ Worker ถือครองอยู่ เช่น คุณควรปิดแฮนเดิลที่เปิดอยู่กับฐานข้อมูลและไฟล์ในตอนนี้ คุณมีกลไก 2 อย่างที่ใช้เพื่อทำความเข้าใจว่าเมื่อใดที่ Worker หยุดทำงาน
Callback onStopped()
WorkManager จะเรียกใช้
ListenableWorker.onStopped()
ทันทีที่หยุด Worker แทนที่เมธอดนี้เพื่อปิด
ทรัพยากรที่คุณอาจถือครองอยู่
พร็อพเพอร์ตี้ isStopped()
คุณสามารถเรียกใช้เมธอด
ListenableWorker.isStopped()
เพื่อตรวจสอบว่าหยุด Worker แล้วหรือยัง
หากคุณดำเนินการที่ใช้เวลานานหรือดำเนินการซ้ำๆ ใน Worker คุณควรตรวจสอบพร็อพเพอร์ตี้นี้บ่อยๆ และใช้เป็นสัญญาณเพื่อหยุดการทำงานโดยเร็วที่สุด
หมายเหตุ: WorkManager จะไม่สนใจ
Result
ที่ตั้งค่าโดย Worker
ที่ได้รับสัญญาณ onStop เนื่องจากระบบถือว่า Worker หยุดทำงานแล้ว
สังเกตสถานะเหตุผลในการหยุด
หากต้องการแก้ไขข้อบกพร่องว่าเหตุใด Worker
จึงหยุดทำงาน คุณสามารถบันทึกเหตุผลที่หยุดทำงานได้โดยการเรียกใช้
WorkInfo.getStopReason()
ดังนี้
Kotlin
workManager.getWorkInfoByIdFlow(syncWorker.id)
.collect { workInfo ->
if (workInfo != null) {
val stopReason = workInfo.stopReason
logStopReason(syncWorker.id, stopReason)
}
}
Java
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(getViewLifecycleOwner(), workInfo -> {
if (workInfo != null) {
int stopReason = workInfo.getStopReason();
logStopReason(syncWorker.id, workInfo.getStopReason());
}
});