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

เมื่อกำหนด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-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());
        }
  });