หลักพื้นฐานของ Android Kotlin 09.2: WorkManager

Codelab นี้เป็นส่วนหนึ่งของหลักสูตรหลักพื้นฐานของ Android Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ Codelab ของหลักสูตรทั้งหมดแสดงอยู่ในหน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin

บทนำ

แอปในโลกแห่งความเป็นจริงส่วนใหญ่ต้องเรียกใช้งานที่ทำอยู่เบื้องหลังซึ่งใช้เวลานาน ตัวอย่างเช่น แอปอาจอัปโหลดไฟล์ไปยังเซิร์ฟเวอร์ ซิงค์ข้อมูลจากเซิร์ฟเวอร์และบันทึกลงในRoomฐานข้อมูล ส่งบันทึกไปยังเซิร์ฟเวอร์ หรือดำเนินการที่มีค่าใช้จ่ายสูงกับข้อมูล การดำเนินการดังกล่าวควรดำเนินการในเบื้องหลังนอกเทรด UI (เทรดหลัก) งานในเบื้องหลังจะใช้ทรัพยากรที่มีจำกัดของอุปกรณ์ เช่น RAM และแบตเตอรี่ ซึ่งอาจส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ไม่ดีหากจัดการไม่ถูกต้อง

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

สิ่งที่คุณควรทราบอยู่แล้ว

  • วิธีใช้คอมโพเนนต์สถาปัตยกรรม Android ViewModel, LiveData และ Room
  • วิธีทำการเปลี่ยนรูปแบบในLiveDataคลาส
  • วิธีสร้างและเปิดใช้ Coroutine
  • วิธีใช้อะแดปเตอร์การเชื่อมในการเชื่อมโยงข้อมูล
  • วิธีโหลดข้อมูลที่แคชไว้โดยใช้รูปแบบที่เก็บ

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้าง Worker ซึ่งแสดงถึงหน่วยงาน
  • วิธีสร้าง WorkRequest เพื่อขอให้ดำเนินการ
  • วิธีเพิ่มข้อจำกัดให้กับ WorkRequest เพื่อกำหนดวิธีและเวลาที่ควรเรียกใช้ Worker
  • วิธีใช้ WorkManager เพื่อกำหนดเวลางานที่ทำงานเบื้องหลัง

สิ่งที่คุณต้องดำเนินการ

  • สร้าง Worker เพื่อเรียกใช้งานงานในเบื้องหลังเพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes จากเครือข่ายล่วงหน้า
  • ตั้งเวลาให้ผู้ปฏิบัติงานทำงานเป็นระยะๆ
  • เพิ่มข้อจํากัดใน WorkRequest
  • ตั้งเวลา WorkRequest เป็นระยะๆ ซึ่งจะดำเนินการวันละครั้ง

ใน Codelab นี้ คุณจะได้ทำงานกับแอป DevBytes ที่พัฒนาใน Codelab ก่อนหน้านี้ (หากไม่มีแอปนี้ คุณสามารถดาวน์โหลดโค้ดเริ่มต้นสำหรับบทเรียนนี้ได้)

แอป DevBytes จะแสดงรายการวิดีโอ DevByte ซึ่งเป็นบทแนะนำสั้นๆ ที่สร้างโดยทีมความสัมพันธ์กับนักพัฒนาแอป Android ของ Google วิดีโอจะแนะนำฟีเจอร์สำหรับนักพัฒนาแอปและแนวทางปฏิบัติแนะนำสำหรับการพัฒนา Android

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

ในงานนี้ คุณจะได้ดาวน์โหลดและตรวจสอบโค้ดเริ่มต้น

ขั้นตอนที่ 1: ดาวน์โหลดและเรียกใช้แอปเริ่มต้น

คุณสามารถทำงานต่อในแอป DevBytes ที่สร้างไว้ใน Codelab ก่อนหน้าได้ (หากมี) หรือจะดาวน์โหลดแอปเริ่มต้นก็ได้

ในงานนี้ คุณจะได้ดาวน์โหลดและเรียกใช้แอปเริ่มต้น รวมถึงตรวจสอบโค้ดเริ่มต้น

  1. หากยังไม่มีแอป DevBytes ให้ดาวน์โหลดโค้ดเริ่มต้นของ DevBytes สำหรับ Codelab นี้จากโปรเจ็กต์ DevBytesRepository ใน GitHub
  2. คลายซิปรหัสและเปิดโปรเจ็กต์ใน Android Studio
  3. เชื่อมต่ออุปกรณ์ทดสอบหรือโปรแกรมจำลองกับอินเทอร์เน็ต หากยังไม่ได้เชื่อมต่อ สร้างและเรียกใช้แอป แอปจะดึงข้อมูลรายการวิดีโอ DevByte จากเครือข่ายและแสดงวิดีโอ
  4. ในแอป ให้แตะวิดีโอเพื่อเปิดในแอป YouTube

ขั้นตอนที่ 2: สำรวจโค้ด

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

  1. ใน Android Studio ให้ขยายแพ็กเกจทั้งหมด
  2. สำรวจdatabaseแพ็กเกจ แพ็กเกจประกอบด้วยเอนทิตีฐานข้อมูลและฐานข้อมูลในเครื่อง ซึ่งใช้ Room
  3. สำรวจrepositoryแพ็กเกจ แพ็กเกจมีคลาส VideosRepository ที่แยกเลเยอร์ข้อมูลออกจากส่วนอื่นๆ ของแอป
  4. สำรวจโค้ดเริ่มต้นที่เหลือด้วยตนเองและด้วยความช่วยเหลือจาก Codelab ก่อนหน้า

WorkManager เป็นหนึ่งในคอมโพเนนต์สถาปัตยกรรมของ Android และเป็นส่วนหนึ่งของ Android Jetpack WorkManager ใช้สำหรับงานในเบื้องหลังที่เลื่อนได้และต้องมีการดำเนินการที่รับประกัน

  • เลื่อนได้หมายความว่าไม่จำเป็นต้องเรียกใช้งานทันที ตัวอย่างเช่น การส่งข้อมูลวิเคราะห์ไปยังเซิร์ฟเวอร์หรือการซิงค์ฐานข้อมูลในเบื้องหลังเป็นงานที่เลื่อนเวลาได้
  • การดำเนินการที่รับประกันหมายความว่างานจะทำงานแม้ว่าแอปจะออกหรืออุปกรณ์จะรีสตาร์ทก็ตาม

ขณะที่ WorkManager ทำงานในเบื้องหลัง ระบบจะดูแลปัญหาความเข้ากันได้และแนวทางปฏิบัติแนะนำสำหรับแบตเตอรี่และสุขภาพของระบบ WorkManager มีความเข้ากันได้ย้อนกลับไปจนถึงระดับ API 14 WorkManager จะเลือกวิธีที่เหมาะสมในการกำหนดเวลางานในเบื้องหลังโดยขึ้นอยู่กับระดับ API ของอุปกรณ์ อาจใช้ JobScheduler (ใน API 23 ขึ้นไป) หรือใช้ทั้ง AlarmManager และ BroadcastReceiver ร่วมกัน

WorkManager ยังช่วยให้คุณกำหนดเกณฑ์เกี่ยวกับเวลาที่งานที่ทำงานเบื้องหลังจะทำงานได้ด้วย เช่น คุณอาจต้องการให้งานทำงานเฉพาะเมื่อสถานะแบตเตอรี่ สถานะเครือข่าย หรือสถานะการชาร์จเป็นไปตามเกณฑ์ที่กำหนด คุณจะได้เรียนรู้วิธีกำหนดข้อจำกัดในภายหลังใน Codelab นี้

ในโค้ดแล็บนี้ คุณจะได้กำหนดเวลางานเพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes จากเครือข่ายล่วงหน้าวันละครั้ง หากต้องการกำหนดเวลางานนี้ คุณต้องใช้ไลบรารี WorkManager

  1. เปิดไฟล์ build.gradle (Module:app) แล้วเพิ่มการขึ้นต่อ WorkManager ลงในโปรเจ็กต์

    หากใช้ไลบรารีเวอร์ชันล่าสุด แอปโซลูชันควรคอมไพล์ได้ตามที่คาดไว้ หากไม่เป็นเช่นนั้น ให้ลองแก้ไขปัญหาหรือกลับไปใช้เวอร์ชันไลบรารีที่แสดงด้านล่าง
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. ซิงค์โปรเจ็กต์และตรวจสอบว่าไม่มีข้อผิดพลาดในการคอมไพล์

ก่อนเพิ่มโค้ดลงในโปรเจ็กต์ โปรดทำความคุ้นเคยกับคลาสต่อไปนี้ในไลบรารี WorkManager

  • Worker
    คลาสนี้คือส่วนที่คุณกำหนดงานจริง (งาน) ที่จะเรียกใช้ในเบื้องหลัง คุณขยายคลาสนี้และแทนที่เมธอด doWork() doWork()เมธอดคือที่ที่คุณใส่โค้ดที่จะดำเนินการในเบื้องหลัง เช่น การซิงค์ข้อมูลกับเซิร์ฟเวอร์หรือการประมวลผลรูปภาพ คุณใช้ Worker ในงานนี้
  • WorkRequest
    คลาสนี้แสดงถึงคำขอให้เรียกใช้ Worker ในเบื้องหลัง ใช้ WorkRequest เพื่อกำหนดค่าวิธีและเวลาที่จะเรียกใช้ Worker Task โดยใช้ความช่วยเหลือจาก Constraints เช่น เสียบปลั๊กอุปกรณ์หรือเชื่อมต่อ Wi-Fi คุณจะติดตั้งใช้งาน WorkRequest ในงานถัดไป
  • WorkManager
    ชั้นเรียนนี้จะกำหนดเวลาและเรียกใช้ WorkRequest WorkManager จะกำหนดเวลาคำของานในลักษณะที่กระจายภาระงานในทรัพยากรของระบบ ในขณะที่ยังคงปฏิบัติตามข้อจำกัดที่คุณระบุ คุณจะติดตั้งใช้งาน WorkManager ในงานถัดไป

ขั้นตอนที่ 1: สร้างผู้ปฏิบัติงาน

ในงานนี้ คุณจะเพิ่ม Worker เพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes ล่วงหน้าในเบื้องหลัง

  1. สร้างแพ็กเกจใหม่ชื่อ work ภายในdevbyteviewer
  2. สร้างคลาส Kotlin ใหม่ชื่อ RefreshDataWorker ในแพ็กเกจ work
  3. ขยายคลาส RefreshDataWorker จากคลาส CoroutineWorker ส่ง context และ WorkerParameters เป็นพารามิเตอร์ของตัวสร้าง
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. หากต้องการแก้ไขข้อผิดพลาดของคลาส Abstract ให้ลบล้างเมธอด doWork() ภายในคลาส RefreshDataWorker
override suspend fun doWork(): Result {
  return Result.success()
}

ฟังก์ชันระงับคือฟังก์ชันที่หยุดชั่วคราวและกลับมาทำงานต่อได้ในภายหลัง ฟังก์ชันระงับสามารถดำเนินการที่ใช้เวลานานและรอให้ดำเนินการเสร็จสมบูรณ์ได้โดยไม่ต้องบล็อกเทรดหลัก

ขั้นตอนที่ 2: ใช้ doWork()

ระบบจะเรียกใช้เมธอด doWork() ภายในคลาส Worker ในเธรดเบื้องหลัง เมธอดนี้จะทำงานแบบซิงโครนัส และควรคืนค่าเป็นออบเจ็กต์ ListenableWorker.Result ระบบ Android ให้เวลา Worker ไม่เกิน 10 นาทีในการดำเนินการให้เสร็จสิ้นและแสดงผลออบเจ็กต์ ListenableWorker.Result หลังจากหมดเวลานี้แล้ว ระบบจะหยุดWorkerโดยอัตโนมัติ

หากต้องการสร้างออบเจ็กต์ ListenableWorker.Result ให้เรียกใช้เมธอดแบบคงที่ต่อไปนี้เพื่อระบุสถานะการทำงานเบื้องหลังที่เสร็จสมบูรณ์

  • Result.success() - งานเสร็จสมบูรณ์
  • Result.failure() - งานเสร็จสมบูรณ์แล้ว แต่ไม่สำเร็จอย่างถาวร
  • Result.retry() - งานพบข้อผิดพลาดชั่วคราวและควรลองอีกครั้ง

ในงานนี้ คุณจะใช้เมธอด doWork() เพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes จากเครือข่าย คุณสามารถใช้วิธีการที่มีอยู่ในคลาส VideosRepository เพื่อดึงข้อมูลจากเครือข่ายได้

  1. ในคลาส RefreshDataWorker ภายใน doWork() ให้สร้างและเริ่มต้นออบเจ็กต์ VideosDatabase และออบเจ็กต์ VideosRepository
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. ในRefreshDataWorkerคลาส ภายในdoWork() เหนือคำสั่งreturn ให้เรียกใช้เมธอดrefreshVideos()ภายในบล็อกtry เพิ่มบันทึกเพื่อติดตามเวลาที่เรียกใช้ Worker
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

หากต้องการแก้ไขข้อผิดพลาด "การอ้างอิงที่ยังไม่ได้รับการแก้ไข" ให้นำเข้า retrofit2.HttpException

  1. นี่คือRefreshDataWorkerคลาสฉบับสมบูรณ์สำหรับใช้อ้างอิง
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Worker กำหนดหน่วยของงาน และ WorkRequest กำหนดวิธีและเวลาที่ควรเรียกใช้งาน การติดตั้งใช้งาน WorkRequest มี 2 รูปแบบที่ชัดเจน ได้แก่

  • ชั้นเรียน OneTimeWorkRequest ใช้สำหรับงานที่ทำครั้งเดียว (งานครั้งเดียวจะเกิดขึ้นเพียงครั้งเดียว)
  • ชั้นเรียน PeriodicWorkRequest เหมาะสำหรับงานที่ต้องทำเป็นระยะๆ หรืองานที่ต้องทำซ้ำเป็นช่วงๆ

งานอาจเป็นแบบครั้งเดียวหรือแบบเป็นระยะๆ ดังนั้นให้เลือกชั้นเรียนตามความเหมาะสม ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดเวลางานที่เกิดซ้ำได้ที่เอกสารประกอบเกี่ยวกับงานที่เกิดซ้ำ

ในงานนี้ คุณจะได้กำหนดและตั้งเวลา WorkRequest เพื่อเรียกใช้ Worker ที่สร้างไว้ในงานก่อนหน้า

ขั้นตอนที่ 1: ตั้งค่างานที่ทำซ้ำ

ภายในแอป Android Application คลาสคือคลาสฐานที่มีคอมโพเนนต์อื่นๆ ทั้งหมด เช่น กิจกรรมและบริการ เมื่อสร้างกระบวนการสำหรับแอปพลิเคชันหรือแพ็กเกจของคุณ ระบบจะสร้างอินสแตนซ์ของคลาส Application (หรือคลาสย่อยของ Application) ก่อนคลาสอื่นๆ

ในแอปตัวอย่างนี้ คลาส DevByteApplication เป็นคลาสย่อยของคลาส Application DevByteApplication ชั้นเรียนเป็นที่ที่เหมาะกับการกำหนดเวลา WorkManager

  1. ในDevByteApplication คลาส ให้สร้างเมธอดชื่อ setupRecurringWork() เพื่อตั้งค่างานพื้นหลังที่เกิดซ้ำ
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. ภายในเมธอด setupRecurringWork() ให้สร้างและเริ่มต้นคำขอการทำงานเป็นระยะเพื่อเรียกใช้วันละครั้งโดยใช้เมธอด PeriodicWorkRequestBuilder() ส่งในRefreshDataWorkerคลาสที่คุณสร้างไว้ในงานก่อนหน้า ส่งช่วงการฝึกซ้ำ 1 โดยมีหน่วยเวลาเป็น TimeUnit.DAYS
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

หากต้องการแก้ไขข้อผิดพลาด ให้นำเข้า java.util.concurrent.TimeUnit

ขั้นตอนที่ 2: กำหนดเวลา WorkRequest ด้วย WorkManager

หลังจากกำหนด WorkRequest แล้ว คุณจะกำหนดเวลาด้วย WorkManager โดยใช้วิธี enqueueUniquePeriodicWork() ได้ วิธีนี้ช่วยให้คุณเพิ่ม PeriodicWorkRequest ที่มีชื่อไม่ซ้ำลงในคิวได้ โดยจะอนุญาตให้ PeriodicWorkRequest ที่มีชื่อเฉพาะชื่อหนึ่งใช้งานได้ครั้งละ 1 รายการเท่านั้น

เช่น คุณอาจต้องการให้การดำเนินการซิงค์ทำงานอยู่เพียงรายการเดียว หากมีการดำเนินการซิงค์ที่รอดำเนินการอยู่ คุณสามารถเลือกที่จะปล่อยให้ดำเนินการต่อไปหรือแทนที่ด้วยงานใหม่โดยใช้ ExistingPeriodicWorkPolicy

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีตั้งเวลา WorkRequest ได้ที่เอกสารประกอบของ WorkManager

  1. ในRefreshDataWorkerชั้นเรียน ให้เพิ่มออบเจ็กต์เสริมที่จุดเริ่มต้นของชั้นเรียน กำหนดชื่อ Worker เพื่อระบุ Worker นี้โดยเฉพาะ
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. ในDevByteApplication คลาส ที่ส่วนท้ายของเมธอด setupRecurringWork() ให้กำหนดเวลางานโดยใช้เมธอด enqueueUniquePeriodicWork() ส่ง enum KEEP สำหรับ ExistingPeriodicWorkPolicy ส่ง repeatingRequest เป็นพารามิเตอร์ PeriodicWorkRequest
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

หากมีงานที่รอดำเนินการ (ยังไม่เสร็จ) ที่มีชื่อเดียวกัน พารามิเตอร์ ExistingPeriodicWorkPolicy.KEEP จะทำให้ WorkManager เก็บงานที่ทำเป็นระยะก่อนหน้าไว้และทิ้งคำของานใหม่

  1. สร้างออบเจ็กต์ CoroutineScope ที่จุดเริ่มต้นของDevByteApplicationคลาส ส่ง Dispatchers.Default เป็นพารามิเตอร์ของเครื่องมือสร้าง
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. ในคลาส DevByteApplication ให้เพิ่มเมธอดใหม่ชื่อ delayedInit() เพื่อเริ่มโครูทีน
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. ภายในเมธอด delayedInit() ให้เรียกใช้ setupRecurringWork()
  2. ย้ายการเริ่มต้น Timber จากเมธอด onCreate() ไปยังเมธอด delayedInit()
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. ในDevByteApplicationคลาส ที่ส่วนท้ายของเมธอด onCreate() ให้เพิ่มการเรียกเมธอด delayedInit()
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. เปิดแผง Logcat ที่ด้านล่างของหน้าต่าง Android Studio ตัวกรองใน RefreshDataWorker
  2. เรียกใช้แอป WorkManager จะกำหนดเวลางานที่ทำซ้ำทันที

    ในแผง Logcat ให้สังเกตข้อความบันทึกที่แสดงว่ามีการกำหนดเวลาคำของานแล้ว จากนั้นคำขอจะทำงานสำเร็จ
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

ระบบจะแสดงบันทึก WM-WorkerWrapper จากคลัง WorkManager คุณจึงเปลี่ยนข้อความบันทึกนี้ไม่ได้

ขั้นตอนที่ 3: (ไม่บังคับ) กำหนดเวลา WorkRequest สำหรับช่วงเวลาขั้นต่ำ

ในขั้นตอนนี้ คุณจะลดช่วงเวลานับจาก 1 วันเป็น 15 นาที คุณทำเช่นนี้เพื่อให้ดูบันทึกสำหรับคำขอให้ทำงานเป็นระยะๆ ที่กำลังดำเนินการได้

  1. ในคลาส DevByteApplication ภายในเมธอด setupRecurringWork() ให้แสดงความคิดเห็นในคำจำกัดความ repeatingRequest ปัจจุบัน เพิ่มคำของานใหม่โดยมีช่วงเวลาการทำซ้ำเป็นระยะๆ ทุก 15 นาที
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. เปิดแผง Logcat ใน Android Studio แล้วกรองใน RefreshDataWorker หากต้องการล้างบันทึกก่อนหน้า ให้คลิกไอคอนล้าง Logcat
  2. เรียกใช้แอป แล้ว WorkManager จะกำหนดเวลาการทำงานที่เกิดซ้ำทันที ในแผง Logcat ให้สังเกตบันทึก - ระบบจะเรียกใช้คำของานทุกๆ 15 นาที รอ 15 นาทีเพื่อดูบันทึกคำของานชุดอื่น คุณจะปล่อยให้แอปทำงานต่อไปหรือปิดก็ได้ แต่ Work Manager ควรยังคงทำงานอยู่

    โปรดทราบว่าบางครั้งช่วงเวลาอาจน้อยกว่า 15 นาที และบางครั้งอาจมากกว่า 15 นาที (เวลาที่แน่นอนขึ้นอยู่กับการเพิ่มประสิทธิภาพแบตเตอรี่ของระบบปฏิบัติการ)
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

ยินดีด้วย คุณสร้างผู้ปฏิบัติงานและกำหนดเวลาคำของานกับ WorkManager แต่มีปัญหาคือคุณไม่ได้ระบุข้อจำกัดใดๆ WorkManager จะกำหนดเวลางานวันละครั้ง แม้ว่าแบตเตอรี่ของอุปกรณ์จะเหลือน้อย อยู่ในโหมดสลีป หรือไม่มีการเชื่อมต่อเครือข่าย ซึ่งจะส่งผลต่อแบตเตอรี่และประสิทธิภาพของอุปกรณ์ และอาจส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ไม่ดี

ในงานถัดไป คุณจะแก้ไขปัญหานี้โดยการเพิ่มข้อจำกัด

ในงานก่อนหน้านี้ คุณใช้ WorkManager เพื่อกำหนดเวลาคำของาน ในงานนี้ คุณจะเพิ่มเกณฑ์สำหรับเวลาที่จะดำเนินการ

เมื่อกำหนด WorkRequest คุณจะระบุข้อจำกัดสำหรับเวลาที่ Worker ควรทำงานได้ เช่น คุณอาจต้องการระบุว่าควรเรียกใช้งานเฉพาะเมื่ออุปกรณ์ไม่ได้ใช้งาน หรือเฉพาะเมื่อเสียบปลั๊กและเชื่อมต่อกับ Wi-Fi คุณยังระบุนโยบายการหยุดชั่วคราวเพื่อลองทำงานอีกครั้งได้ด้วย ข้อจำกัดที่รองรับคือเมธอดการตั้งค่าใน Constraints.Builder ดูข้อมูลเพิ่มเติมได้ที่การกำหนดคำของาน

ขั้นตอนที่ 1: เพิ่มออบเจ็กต์ข้อจำกัดและตั้งค่าข้อจำกัด 1 รายการ

ในขั้นตอนนี้ คุณจะสร้างออบเจ็กต์ Constraints และตั้งค่าข้อจำกัด 1 อย่างในออบเจ็กต์ ซึ่งก็คือข้อจำกัดประเภทเครือข่าย (การสังเกตบันทึกที่มีข้อจำกัดเพียงข้อเดียวจะง่ายกว่า ในขั้นตอนถัดไป คุณจะเพิ่มข้อจำกัดอื่นๆ ได้)

  1. ในคลาส DevByteApplication ที่จุดเริ่มต้นของ setupRecurringWork() ให้กำหนด val ของประเภท Constraints ใช้เมธอด Constraints.Builder()
val constraints = Constraints.Builder()

หากต้องการแก้ไขข้อผิดพลาด ให้นำเข้า androidx.work.Constraints

  1. ใช้วิธี setRequiredNetworkType() เพื่อเพิ่มข้อจำกัดประเภทเครือข่ายไปยังออบเจ็กต์ constraints ใช้ UNMETERED enum เพื่อให้คำขอการทำงานจะทำงานเมื่ออุปกรณ์อยู่ในเครือข่ายที่ไม่จำกัดปริมาณการใช้งานเท่านั้น
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. ใช้วิธี build() เพื่อสร้างข้อจำกัดจากเครื่องมือสร้าง
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

ตอนนี้คุณต้องตั้งค่าออบเจ็กต์ Constraints ที่สร้างขึ้นใหม่ให้กับคำของาน

  1. ในDevByteApplication คลาส ภายในเมธอด setupRecurringWork() ให้ตั้งค่าออบเจ็กต์ Constraints เป็นคำขอเวิร์กแบบเป็นระยะ repeatingRequest หากต้องการตั้งค่าข้อจำกัด ให้เพิ่มเมธอด setConstraints() ไว้เหนือการเรียกเมธอด build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

ขั้นตอนที่ 2: เรียกใช้แอปและสังเกตบันทึก

ในขั้นตอนนี้ คุณจะเรียกใช้แอปและสังเกตเห็นว่าคำของานที่จำกัดกำลังทำงานอยู่เบื้องหลังเป็นระยะๆ

  1. ถอนการติดตั้งแอปจากอุปกรณ์หรือโปรแกรมจำลองเพื่อยกเลิกงานที่กำหนดเวลาไว้ก่อนหน้านี้
  2. เปิดแผง Logcat ใน Android Studio ในแผง Logcat ให้ล้างบันทึกก่อนหน้าโดยคลิกไอคอนล้าง Logcat ทางด้านซ้าย ตัวกรองใน work
  3. ปิด Wi-Fi ในอุปกรณ์หรือโปรแกรมจำลองเพื่อให้คุณเห็นวิธีการทำงานของข้อจำกัด โค้ดปัจจุบันกำหนดข้อจำกัดเพียงข้อเดียว ซึ่งระบุว่าคำขอควรทำงานในเครือข่ายที่ไม่จำกัดปริมาณการใช้งานเท่านั้น เนื่องจาก Wi-Fi ปิดอยู่ อุปกรณ์จึงไม่ได้เชื่อมต่อกับเครือข่ายแบบคิดค่าบริการตามปริมาณการใช้งานหรือไม่คิดค่าบริการตามปริมาณการใช้งาน ดังนั้นข้อจำกัดนี้จึงไม่เป็นไปตามข้อกำหนด
  4. เรียกใช้แอปและสังเกตที่แผง Logcat WorkManager จะกำหนดเวลางานในเบื้องหลังทันที เนื่องจากไม่เป็นไปตามข้อจำกัดของเครือข่าย ระบบจึงไม่เรียกใช้ Task
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. เปิด Wi-Fi ในอุปกรณ์หรือโปรแกรมจำลอง แล้วดูที่แผง Logcat ตอนนี้ระบบจะเรียกใช้งานที่กำหนดเวลาไว้ในเบื้องหลังทุกๆ 15 นาทีโดยประมาณ ตราบใดที่เป็นไปตามข้อจำกัดของเครือข่าย
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

ขั้นตอนที่ 3: เพิ่มข้อจำกัด

ในขั้นตอนนี้ คุณจะเพิ่มข้อจำกัดต่อไปนี้ลงใน PeriodicWorkRequest

  • แบตเตอรี่ไม่เหลือน้อย
  • การชาร์จอุปกรณ์
  • อุปกรณ์ไม่ได้ใช้งาน ใช้ได้เฉพาะใน API ระดับ 23 (Android M) ขึ้นไป

นำสิ่งต่อไปนี้ไปใช้ในDevByteApplication class

  1. ในคลาส DevByteApplication ภายในเมธอด setupRecurringWork() ให้ระบุว่าคำขอการทำงานควรทำงานเฉพาะในกรณีที่แบตเตอรี่ไม่อยู่ในระดับต่ำ เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอด build() และใช้เมธอด setRequiresBatteryNotLow()
.setRequiresBatteryNotLow(true)
  1. อัปเดตคำของานเพื่อให้ทำงานเฉพาะเมื่ออุปกรณ์กำลังชาร์จ เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอด build() และใช้เมธอด setRequiresCharging()
.setRequiresCharging(true)
  1. อัปเดตคำของานเพื่อให้ทำงานเฉพาะเมื่ออุปกรณ์ไม่มีการใช้งาน เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอด build() และใช้เมธอด setRequiresDeviceIdle() ข้อจำกัดนี้จะเรียกใช้คำของานเมื่อผู้ใช้ไม่ได้ใช้อุปกรณ์อยู่เท่านั้น ฟีเจอร์นี้ใช้ได้ใน Android 6.0 (Marshmallow) ขึ้นไปเท่านั้น ดังนั้นให้เพิ่มเงื่อนไขสำหรับ SDK เวอร์ชัน M ขึ้นไป
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

นี่คือคำจำกัดความที่สมบูรณ์ของออบเจ็กต์ constraints

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. ในเมธอด setupRecurringWork() ให้เปลี่ยนช่วงเวลาการส่งคำขอกลับเป็นวันละครั้ง
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

ต่อไปนี้คือการใช้งานเมธอด setupRecurringWork() อย่างสมบูรณ์พร้อมบันทึกเพื่อให้คุณติดตามได้เมื่อมีการกำหนดเวลาคำของานเป็นระยะ

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. หากต้องการนำคำของานที่กำหนดเวลาก่อนหน้านี้ออก ให้ถอนการติดตั้งแอป DevBytes จากอุปกรณ์หรือโปรแกรมจำลอง
  2. เรียกใช้แอป แล้ว WorkManager จะกำหนดเวลาคำของานทันที คำของานจะทำงานวันละครั้งเมื่อเป็นไปตามข้อจำกัดทั้งหมด
  3. คำขอการทำงานนี้จะทำงานในเบื้องหลังตราบใดที่แอปยังคงติดตั้งอยู่ แม้ว่าแอปจะไม่ได้ทำงานก็ตาม ด้วยเหตุนี้ คุณจึงควรถอนการติดตั้งแอปจากโทรศัพท์

เก่งมาก! คุณได้ติดตั้งใช้งานและกำหนดเวลาคำของานที่เป็นมิตรกับแบตเตอรี่สำหรับการดึงข้อมูลวิดีโอล่วงหน้าทุกวันในแอป DevBytes แล้ว WorkManager จะกำหนดเวลาและเรียกใช้งาน โดยเพิ่มประสิทธิภาพทรัพยากรของระบบ ผู้ใช้และแบตเตอรี่จะพึงพอใจเป็นอย่างมาก

โปรเจ็กต์ Android Studio: DevBytesWorkManager

  • WorkManager API ช่วยให้กำหนดเวลางานแบบอะซิงโครนัสที่เลื่อนได้ซึ่งต้องเรียกใช้อย่างน่าเชื่อถือได้ง่าย
  • แอปในโลกแห่งความเป็นจริงส่วนใหญ่ต้องเรียกใช้งานที่ทำอยู่เบื้องหลังซึ่งใช้เวลานาน หากต้องการกำหนดเวลางานในเบื้องหลังอย่างมีประสิทธิภาพและเพิ่มประสิทธิภาพ ให้ใช้ WorkManager
  • คลาสหลักในไลบรารี WorkManager คือ Worker, WorkRequest และ WorkManager
  • Worker คลาสแสดงถึงหน่วยของงาน หากต้องการใช้ฟีเจอร์งานในเบื้องหลัง ให้ขยายคลาส Worker และลบล้างเมธอด doWork()
  • คลาส WorkRequest แสดงถึงคำขอให้ดำเนินการหน่วยงาน WorkRequest เป็นคลาสพื้นฐานสำหรับการระบุพารามิเตอร์สำหรับงานที่คุณกำหนดเวลาไว้ใน WorkManager
  • WorkRequest มีการใช้งานจริง 2 แบบ ได้แก่ OneTimeWorkRequest สำหรับงานแบบครั้งเดียว และ PeriodicWorkRequest สำหรับคำของานเป็นระยะ
  • เมื่อกำหนด WorkRequest คุณสามารถระบุ Constraints เพื่อระบุเวลาที่ Worker ควรทำงาน ข้อจำกัดรวมถึงสิ่งต่างๆ เช่น อุปกรณ์เสียบปลั๊กอยู่หรือไม่ อุปกรณ์ไม่ได้ใช้งานอยู่หรือไม่ หรือเชื่อมต่อ Wi-Fi อยู่หรือไม่
  • หากต้องการเพิ่มข้อจำกัดให้กับ WorkRequest ให้ใช้วิธีการตั้งค่าที่ระบุไว้ในเอกสารประกอบของ Constraints.Builder เช่น หากต้องการระบุว่าไม่ควรเรียกใช้ WorkRequest หากแบตเตอรี่ของอุปกรณ์เหลือน้อย ให้ใช้วิธีการตั้งค่า setRequiresBatteryNotLow()
  • หลังจากกำหนด WorkRequest แล้ว ให้ส่งต่อภารกิจไปยังระบบ Android โดยให้กำหนดเวลางานโดยใช้วิธีการใดวิธีการหนึ่งในWorkManager enqueue
  • เวลาที่แน่นอนที่ Worker จะดำเนินการขึ้นอยู่กับข้อจำกัดที่ใช้ใน WorkRequest และการเพิ่มประสิทธิภาพระบบ WorkManager ออกแบบมาเพื่อให้ทำงานได้ดีที่สุดภายใต้ข้อจำกัดเหล่านี้

หลักสูตร Udacity:

เอกสารประกอบสำหรับนักพัฒนาแอป Android

อื่นๆ:

ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ Codelab นี้เป็นส่วนหนึ่งของหลักสูตรที่สอนโดยผู้สอน ผู้สอนมีหน้าที่ดำเนินการต่อไปนี้

  • มอบหมายการบ้านหากจำเป็น
  • สื่อสารกับนักเรียนเกี่ยวกับวิธีส่งงานที่ได้รับมอบหมาย
  • ให้คะแนนงานการบ้าน

ผู้สอนสามารถใช้คำแนะนำเหล่านี้ได้มากน้อยตามที่ต้องการ และควรมีอิสระในการมอบหมายการบ้านอื่นๆ ที่เห็นว่าเหมาะสม

หากคุณกำลังทำ Codelab นี้ด้วยตนเอง โปรดใช้แบบฝึกหัดเหล่านี้เพื่อทดสอบความรู้ของคุณ

คำถามที่ 1

การใช้งานจริงของคลาส WorkRequest คืออะไร

OneTimeWorkPeriodicRequest

OneTimeWorkRequest และ PeriodicWorkRequest

OneTimeWorkRequest และ RecurringWorkRequest

OneTimeOffWorkRequest และ RecurringWorkRequest

คำถามที่ 2

WorkManager ใช้คลาสใดต่อไปนี้เพื่อกำหนดเวลางานที่ทำงานอยู่เบื้องหลังใน API 23 ขึ้นไป

▢ เฉพาะ JobScheduler

BroadcastReceiver และ AlarmManager

AlarmManager และ JobScheduler

Scheduler และ BroadcastReceiver

คำถามที่ 3

คุณใช้ API ใดในการเพิ่มข้อจำกัดให้กับ WorkRequest

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

เริ่มบทเรียนถัดไป: 10.1 สไตล์และธีม

ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin