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

เมื่อกำหนด defined your Worker และ your 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 รายการสำหรับการจัดการความขัดแย้ง

งานที่มีอยู่จะกลายเป็น ข้อกำหนดเบื้องต้น สำหรับงานใหม่ หากงานที่มีอยู่เปลี่ยนเป็น 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 ยังทำงานร่วมกับ getWorkInfosLiveData() ซึ่งเทียบเท่ากับ LiveData และ getWorkInfosFlow() ซึ่งเทียบเท่ากับ Flow

การยกเลิกและการหยุดงาน

หากไม่ต้องการให้งานที่จัดคิวไว้ก่อนหน้านี้ทำงานอีกต่อไป คุณสามารถขอให้ยกเลิกงานได้ คุณสามารถยกเลิกงานได้ตาม 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 invokes ListenableWorker.onStopped() ทันทีที่ Worker หยุดทำงาน คุณควรลบล้างเมธอดนี้เพื่อปิดทรัพยากรที่คุณอาจใช้

พร็อพเพอร์ตี้ isStopped()

คุณสามารถเรียกใช้เมธอด ListenableWorker.isStopped() เพื่อตรวจสอบว่า Worker หยุดทำงานแล้วหรือยัง หากคุณดำเนินการที่ใช้เวลานานหรือดำเนินการซ้ำๆ ใน Worker คุณควรตรวจสอบพร็อพเพอร์ตี้นี้บ่อยๆ และใช้เป็นสัญญาณเพื่อหยุดงานโดยเร็วที่สุด

หมายเหตุ: WorkManager จะละเว้น Result ที่ตั้งค่าโดย Worker ที่ได้รับสัญญาณ onStop เนื่องจากระบบถือว่า Worker หยุดทำงานแล้ว