การจัดการงาน

เมื่อกำหนด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 สร้างขึ้นโดยอัตโนมัติ ชื่อที่ไม่ซ้ำจะเชื่อมโยงกับผลงานเพียงรายการเดียวเท่านั้น ซึ่งต่างจากแท็ก

คุณสามารถใช้ผลงานที่ไม่ซ้ำกันกับงานแบบครั้งเดียวและงานที่ทำเป็นระยะได้ คุณสร้างลำดับงานที่ไม่ซ้ำกันได้โดยเรียกใช้เมธอดใดเมธอดหนึ่งต่อไปนี้ ทั้งนี้ขึ้นอยู่กับว่าคุณกำลังกำหนดเวลาให้งานทำซ้ำหรือทำครั้งเดียว

ทั้ง 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 ตัวอย่างเช่น (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 หยุดทำงานแล้ว