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 ก่อนหน้าได้ (หากมี) หรือจะดาวน์โหลดแอปเริ่มต้นก็ได้
ในงานนี้ คุณจะได้ดาวน์โหลดและเรียกใช้แอปเริ่มต้น รวมถึงตรวจสอบโค้ดเริ่มต้น
- หากยังไม่มีแอป DevBytes ให้ดาวน์โหลดโค้ดเริ่มต้นของ DevBytes สำหรับ Codelab นี้จากโปรเจ็กต์ DevBytesRepository ใน GitHub
- คลายซิปรหัสและเปิดโปรเจ็กต์ใน Android Studio
- เชื่อมต่ออุปกรณ์ทดสอบหรือโปรแกรมจำลองกับอินเทอร์เน็ต หากยังไม่ได้เชื่อมต่อ สร้างและเรียกใช้แอป แอปจะดึงข้อมูลรายการวิดีโอ DevByte จากเครือข่ายและแสดงวิดีโอ
- ในแอป ให้แตะวิดีโอเพื่อเปิดในแอป YouTube
ขั้นตอนที่ 2: สำรวจโค้ด
แอปเริ่มต้นมาพร้อมกับโค้ดจำนวนมากที่แนะนำในโค้ดแล็บก่อนหน้า โค้ดเริ่มต้นสำหรับโค้ดแล็บนี้มีโมดูลเครือข่าย อินเทอร์เฟซผู้ใช้ แคชแบบออฟไลน์ และที่เก็บ คุณสามารถมุ่งเน้นไปที่การกำหนดเวลางานในเบื้องหลังโดยใช้ WorkManager 
- ใน Android Studio ให้ขยายแพ็กเกจทั้งหมด
- สำรวจdatabaseแพ็กเกจ แพ็กเกจประกอบด้วยเอนทิตีฐานข้อมูลและฐานข้อมูลในเครื่อง ซึ่งใช้Room
- สำรวจrepositoryแพ็กเกจ แพ็กเกจมีคลาสVideosRepositoryที่แยกเลเยอร์ข้อมูลออกจากส่วนอื่นๆ ของแอป
- สำรวจโค้ดเริ่มต้นที่เหลือด้วยตนเองและด้วยความช่วยเหลือจาก Codelab ก่อนหน้า
WorkManager เป็นหนึ่งในคอมโพเนนต์สถาปัตยกรรมของ Android และเป็นส่วนหนึ่งของ Android Jetpack WorkManager ใช้สำหรับงานในเบื้องหลังที่เลื่อนได้และต้องมีการดำเนินการที่รับประกัน
- เลื่อนได้หมายความว่าไม่จำเป็นต้องเรียกใช้งานทันที ตัวอย่างเช่น การส่งข้อมูลวิเคราะห์ไปยังเซิร์ฟเวอร์หรือการซิงค์ฐานข้อมูลในเบื้องหลังเป็นงานที่เลื่อนเวลาได้
- การดำเนินการที่รับประกันหมายความว่างานจะทำงานแม้ว่าแอปจะออกหรืออุปกรณ์จะรีสตาร์ทก็ตาม

ขณะที่ WorkManager ทำงานในเบื้องหลัง ระบบจะดูแลปัญหาความเข้ากันได้และแนวทางปฏิบัติแนะนำสำหรับแบตเตอรี่และสุขภาพของระบบ WorkManager มีความเข้ากันได้ย้อนกลับไปจนถึงระดับ API 14 WorkManager จะเลือกวิธีที่เหมาะสมในการกำหนดเวลางานในเบื้องหลังโดยขึ้นอยู่กับระดับ API ของอุปกรณ์ อาจใช้ JobScheduler (ใน API 23 ขึ้นไป) หรือใช้ทั้ง AlarmManager และ BroadcastReceiver ร่วมกัน
WorkManager ยังช่วยให้คุณกำหนดเกณฑ์เกี่ยวกับเวลาที่งานที่ทำงานเบื้องหลังจะทำงานได้ด้วย เช่น คุณอาจต้องการให้งานทำงานเฉพาะเมื่อสถานะแบตเตอรี่ สถานะเครือข่าย หรือสถานะการชาร์จเป็นไปตามเกณฑ์ที่กำหนด คุณจะได้เรียนรู้วิธีกำหนดข้อจำกัดในภายหลังใน Codelab นี้
ในโค้ดแล็บนี้ คุณจะได้กำหนดเวลางานเพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes จากเครือข่ายล่วงหน้าวันละครั้ง หากต้องการกำหนดเวลางานนี้ คุณต้องใช้ไลบรารี WorkManager
- เปิดไฟล์ build.gradle (Module:app)แล้วเพิ่มการขึ้นต่อWorkManagerลงในโปรเจ็กต์
 หากใช้ไลบรารีเวอร์ชันล่าสุด แอปโซลูชันควรคอมไพล์ได้ตามที่คาดไว้ หากไม่เป็นเช่นนั้น ให้ลองแก้ไขปัญหาหรือกลับไปใช้เวอร์ชันไลบรารีที่แสดงด้านล่าง
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"- ซิงค์โปรเจ็กต์และตรวจสอบว่าไม่มีข้อผิดพลาดในการคอมไพล์
ก่อนเพิ่มโค้ดลงในโปรเจ็กต์ โปรดทำความคุ้นเคยกับคลาสต่อไปนี้ในไลบรารี WorkManager
- Worker
 คลาสนี้คือส่วนที่คุณกำหนดงานจริง (งาน) ที่จะเรียกใช้ในเบื้องหลัง คุณขยายคลาสนี้และแทนที่เมธอด- doWork()- doWork()เมธอดคือที่ที่คุณใส่โค้ดที่จะดำเนินการในเบื้องหลัง เช่น การซิงค์ข้อมูลกับเซิร์ฟเวอร์หรือการประมวลผลรูปภาพ คุณใช้- Workerในงานนี้
- WorkRequest
 คลาสนี้แสดงถึงคำขอให้เรียกใช้ Worker ในเบื้องหลัง ใช้- WorkRequestเพื่อกำหนดค่าวิธีและเวลาที่จะเรียกใช้ Worker Task โดยใช้ความช่วยเหลือจาก- Constraintsเช่น เสียบปลั๊กอุปกรณ์หรือเชื่อมต่อ Wi-Fi คุณจะติดตั้งใช้งาน- WorkRequestในงานถัดไป
- WorkManager
 ชั้นเรียนนี้จะกำหนดเวลาและเรียกใช้- WorkRequest- WorkManagerจะกำหนดเวลาคำของานในลักษณะที่กระจายภาระงานในทรัพยากรของระบบ ในขณะที่ยังคงปฏิบัติตามข้อจำกัดที่คุณระบุ คุณจะติดตั้งใช้งาน- WorkManagerในงานถัดไป
ขั้นตอนที่ 1: สร้างผู้ปฏิบัติงาน
ในงานนี้ คุณจะเพิ่ม Worker เพื่อดึงข้อมูลเพลย์ลิสต์วิดีโอ DevBytes ล่วงหน้าในเบื้องหลัง
- สร้างแพ็กเกจใหม่ชื่อ workภายในdevbyteviewer
- สร้างคลาส Kotlin ใหม่ชื่อ RefreshDataWorkerในแพ็กเกจwork
- ขยายคลาส RefreshDataWorkerจากคลาสCoroutineWorkerส่งcontextและWorkerParametersเป็นพารามิเตอร์ของตัวสร้าง
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}- หากต้องการแก้ไขข้อผิดพลาดของคลาส 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 เพื่อดึงข้อมูลจากเครือข่ายได้
- ในคลาส RefreshDataWorkerภายในdoWork()ให้สร้างและเริ่มต้นออบเจ็กต์VideosDatabaseและออบเจ็กต์VideosRepository
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)
   return Result.success()
}- ใน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
- นี่คือ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
- ในDevByteApplicationคลาส ให้สร้างเมธอดชื่อsetupRecurringWork()เพื่อตั้งค่างานพื้นหลังที่เกิดซ้ำ
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}- ภายในเมธอด 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
- ในRefreshDataWorkerชั้นเรียน ให้เพิ่มออบเจ็กต์เสริมที่จุดเริ่มต้นของชั้นเรียน กำหนดชื่อ Worker เพื่อระบุ Worker นี้โดยเฉพาะ
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}- ในDevByteApplicationคลาส ที่ส่วนท้ายของเมธอดsetupRecurringWork()ให้กำหนดเวลางานโดยใช้เมธอดenqueueUniquePeriodicWork()ส่ง enumKEEPสำหรับ ExistingPeriodicWorkPolicy ส่งrepeatingRequestเป็นพารามิเตอร์PeriodicWorkRequest
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)หากมีงานที่รอดำเนินการ (ยังไม่เสร็จ) ที่มีชื่อเดียวกัน พารามิเตอร์ ExistingPeriodicWorkPolicy.KEEP จะทำให้ WorkManager เก็บงานที่ทำเป็นระยะก่อนหน้าไว้และทิ้งคำของานใหม่
- สร้างออบเจ็กต์ CoroutineScopeที่จุดเริ่มต้นของDevByteApplicationคลาส ส่งDispatchers.Defaultเป็นพารามิเตอร์ของเครื่องมือสร้าง
private val applicationScope = CoroutineScope(Dispatchers.Default)- ในคลาส DevByteApplicationให้เพิ่มเมธอดใหม่ชื่อdelayedInit()เพื่อเริ่มโครูทีน
private fun delayedInit() {
   applicationScope.launch {
   }
}- ภายในเมธอด delayedInit()ให้เรียกใช้setupRecurringWork()
- ย้ายการเริ่มต้น Timber จากเมธอด onCreate()ไปยังเมธอดdelayedInit()
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}- ในDevByteApplicationคลาส ที่ส่วนท้ายของเมธอดonCreate()ให้เพิ่มการเรียกเมธอดdelayedInit()
override fun onCreate() {
   super.onCreate()
   delayedInit()
}- เปิดแผง Logcat ที่ด้านล่างของหน้าต่าง Android Studio ตัวกรองใน RefreshDataWorker
- เรียกใช้แอป 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 นาที คุณทำเช่นนี้เพื่อให้ดูบันทึกสำหรับคำขอให้ทำงานเป็นระยะๆ ที่กำลังดำเนินการได้
- ในคลาส DevByteApplicationภายในเมธอดsetupRecurringWork()ให้แสดงความคิดเห็นในคำจำกัดความrepeatingRequestปัจจุบัน เพิ่มคำของานใหม่โดยมีช่วงเวลาการทำซ้ำเป็นระยะๆ ทุก15นาที
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()- เปิดแผง Logcat ใน Android Studio แล้วกรองใน RefreshDataWorkerหากต้องการล้างบันทึกก่อนหน้า ให้คลิกไอคอนล้าง Logcat 
- เรียกใช้แอป แล้ว 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 อย่างในออบเจ็กต์ ซึ่งก็คือข้อจำกัดประเภทเครือข่าย (การสังเกตบันทึกที่มีข้อจำกัดเพียงข้อเดียวจะง่ายกว่า ในขั้นตอนถัดไป คุณจะเพิ่มข้อจำกัดอื่นๆ ได้)
- ในคลาส DevByteApplicationที่จุดเริ่มต้นของsetupRecurringWork()ให้กำหนดvalของประเภทConstraintsใช้เมธอดConstraints.Builder()
val constraints = Constraints.Builder()หากต้องการแก้ไขข้อผิดพลาด ให้นำเข้า androidx.work.Constraints
- ใช้วิธี setRequiredNetworkType()เพื่อเพิ่มข้อจำกัดประเภทเครือข่ายไปยังออบเจ็กต์constraintsใช้UNMETEREDenum เพื่อให้คำขอการทำงานจะทำงานเมื่ออุปกรณ์อยู่ในเครือข่ายที่ไม่จำกัดปริมาณการใช้งานเท่านั้น
.setRequiredNetworkType(NetworkType.UNMETERED)- ใช้วิธี build()เพื่อสร้างข้อจำกัดจากเครื่องมือสร้าง
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()ตอนนี้คุณต้องตั้งค่าออบเจ็กต์ Constraints ที่สร้างขึ้นใหม่ให้กับคำของาน
- ในDevByteApplicationคลาส ภายในเมธอดsetupRecurringWork()ให้ตั้งค่าออบเจ็กต์Constraintsเป็นคำขอเวิร์กแบบเป็นระยะrepeatingRequestหากต้องการตั้งค่าข้อจำกัด ให้เพิ่มเมธอดsetConstraints()ไว้เหนือการเรียกเมธอดbuild()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()ขั้นตอนที่ 2: เรียกใช้แอปและสังเกตบันทึก
ในขั้นตอนนี้ คุณจะเรียกใช้แอปและสังเกตเห็นว่าคำของานที่จำกัดกำลังทำงานอยู่เบื้องหลังเป็นระยะๆ
- ถอนการติดตั้งแอปจากอุปกรณ์หรือโปรแกรมจำลองเพื่อยกเลิกงานที่กำหนดเวลาไว้ก่อนหน้านี้
- เปิดแผง Logcat ใน Android Studio ในแผง Logcat ให้ล้างบันทึกก่อนหน้าโดยคลิกไอคอนล้าง Logcat ทางด้านซ้าย ตัวกรองใน ทางด้านซ้าย ตัวกรองในwork
- ปิด Wi-Fi ในอุปกรณ์หรือโปรแกรมจำลองเพื่อให้คุณเห็นวิธีการทำงานของข้อจำกัด โค้ดปัจจุบันกำหนดข้อจำกัดเพียงข้อเดียว ซึ่งระบุว่าคำขอควรทำงานในเครือข่ายที่ไม่จำกัดปริมาณการใช้งานเท่านั้น เนื่องจาก Wi-Fi ปิดอยู่ อุปกรณ์จึงไม่ได้เชื่อมต่อกับเครือข่ายแบบคิดค่าบริการตามปริมาณการใช้งานหรือไม่คิดค่าบริการตามปริมาณการใช้งาน ดังนั้นข้อจำกัดนี้จึงไม่เป็นไปตามข้อกำหนด
- เรียกใช้แอปและสังเกตที่แผง Logcat WorkManagerจะกำหนดเวลางานในเบื้องหลังทันที เนื่องจากไม่เป็นไปตามข้อจำกัดของเครือข่าย ระบบจึงไม่เรียกใช้ Task
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
- เปิด 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
- ในคลาส DevByteApplicationภายในเมธอดsetupRecurringWork()ให้ระบุว่าคำขอการทำงานควรทำงานเฉพาะในกรณีที่แบตเตอรี่ไม่อยู่ในระดับต่ำ เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอดbuild()และใช้เมธอดsetRequiresBatteryNotLow()
.setRequiresBatteryNotLow(true)- อัปเดตคำของานเพื่อให้ทำงานเฉพาะเมื่ออุปกรณ์กำลังชาร์จ เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอด build()และใช้เมธอดsetRequiresCharging()
.setRequiresCharging(true)- อัปเดตคำของานเพื่อให้ทำงานเฉพาะเมื่ออุปกรณ์ไม่มีการใช้งาน เพิ่มข้อจำกัดก่อนการเรียกใช้เมธอด 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()- ในเมธอด 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)
   }- หากต้องการนำคำของานที่กำหนดเวลาก่อนหน้านี้ออก ให้ถอนการติดตั้งแอป DevBytes จากอุปกรณ์หรือโปรแกรมจำลอง
- เรียกใช้แอป แล้ว WorkManagerจะกำหนดเวลาคำของานทันที คำของานจะทำงานวันละครั้งเมื่อเป็นไปตามข้อจำกัดทั้งหมด
- คำขอการทำงานนี้จะทำงานในเบื้องหลังตราบใดที่แอปยังคงติดตั้งอยู่ แม้ว่าแอปจะไม่ได้ทำงานก็ตาม ด้วยเหตุนี้ คุณจึงควรถอนการติดตั้งแอปจากโทรศัพท์
เก่งมาก! คุณได้ติดตั้งใช้งานและกำหนดเวลาคำของานที่เป็นมิตรกับแบตเตอรี่สำหรับการดึงข้อมูลวิดีโอล่วงหน้าทุกวันในแอป DevBytes แล้ว WorkManager จะกำหนดเวลาและเรียกใช้งาน โดยเพิ่มประสิทธิภาพทรัพยากรของระบบ ผู้ใช้และแบตเตอรี่จะพึงพอใจเป็นอย่างมาก
โปรเจ็กต์ Android Studio: DevBytesWorkManager
- WorkManagerAPI ช่วยให้กำหนดเวลางานแบบอะซิงโครนัสที่เลื่อนได้ซึ่งต้องเรียกใช้อย่างน่าเชื่อถือได้ง่าย
- แอปในโลกแห่งความเป็นจริงส่วนใหญ่ต้องเรียกใช้งานที่ทำอยู่เบื้องหลังซึ่งใช้เวลานาน หากต้องการกำหนดเวลางานในเบื้องหลังอย่างมีประสิทธิภาพและเพิ่มประสิทธิภาพ ให้ใช้ 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 โดยให้กำหนดเวลางานโดยใช้วิธีการใดวิธีการหนึ่งในWorkManagerenqueue
- เวลาที่แน่นอนที่ Workerจะดำเนินการขึ้นอยู่กับข้อจำกัดที่ใช้ในWorkRequestและการเพิ่มประสิทธิภาพระบบWorkManagerออกแบบมาเพื่อให้ทำงานได้ดีที่สุดภายใต้ข้อจำกัดเหล่านี้
หลักสูตร Udacity:
เอกสารประกอบสำหรับนักพัฒนาแอป Android
- การกำหนดคำของาน
- WorkManager
- เริ่มต้นใช้งาน WorkManager
- งานที่ทำซ้ำ
- คำแนะนำเกี่ยวกับการประมวลผลในเบื้องหลัง
อื่นๆ:
ส่วนนี้แสดงรายการการบ้านที่เป็นไปได้สำหรับนักเรียน/นักศึกษาที่กำลังทำ 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()
เริ่มบทเรียนถัดไป: 
ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab หลักพื้นฐานของ Android Kotlin