Codelab นี้เป็นส่วนหนึ่งของหลักสูตรพื้นฐานเกี่ยวกับ Kotlin ใน Android คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้ หากทํางานผ่าน Codelab ตามลําดับ Codelab ของหลักสูตรทั้งหมดจะแสดงอยู่ในหน้า Landing Page ของ Codelab ของ Android Kotlin Fundamentals
บทนำ
แอปในชีวิตจริงส่วนใหญ่จะต้องทํางานในเบื้องหลังที่ยาวนาน เช่น แอปอาจอัปโหลดไฟล์ไปยังเซิร์ฟเวอร์ ซิงค์ข้อมูลจากเซิร์ฟเวอร์ และบันทึกลงในฐานข้อมูล Room
ส่งบันทึกไปยังเซิร์ฟเวอร์ หรือดําเนินการกับข้อมูลราคาแพง การดําเนินการดังกล่าวควรดําเนินการในเบื้องหลัง แยกจากชุดข้อความ UI (ชุดข้อความหลัก) งานในเบื้องหลังจะใช้ทรัพยากรที่จํากัดของอุปกรณ์ เช่น RAM และแบตเตอรี่ ซึ่งอาจส่งผลให้ผู้ใช้ได้รับประสบการณ์ใช้งานที่ไม่ดี
ใน Codelab นี้ คุณดูวิธีใช้ WorkManager
เพื่อตั้งเวลางานในเบื้องหลังอย่างเหมาะสมและมีประสิทธิภาพ หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับโซลูชันอื่นๆ ที่ใช้ได้สําหรับการประมวลผลในเบื้องหลังใน Android โปรดดูคําแนะนําเกี่ยวกับการประมวลผลในเบื้องหลัง
สิ่งที่ควรทราบอยู่แล้ว
- วิธีใช้คอมโพเนนต์สถาปัตยกรรม Android
ViewModel
,LiveData
และRoom
- วิธีเปลี่ยนรูปแบบชั้นเรียน
LiveData
- วิธีสร้างและเปิดตัวโครูทีน
- วิธีใช้อะแดปเตอร์การเชื่อมโยงในการเชื่อมโยงข้อมูล
- วิธีโหลดข้อมูลที่แคชไว้โดยใช้รูปแบบที่เก็บ
สิ่งที่คุณจะได้เรียนรู้
- วิธีสร้าง
Worker
ซึ่งแสดงถึงหน่วยงาน - วิธีสร้าง
WorkRequest
เพื่อขอให้ดําเนินการ - วิธีเพิ่มข้อจํากัดลงใน
WorkRequest
เพื่อกําหนดเวลาและผู้ปฏิบัติงานควรทํางาน - วิธีใช้
WorkManager
เพื่อตั้งเวลางานในเบื้องหลัง
สิ่งที่คุณจะทํา
- สร้างผู้ปฏิบัติงานเพื่อทํางานเบื้องหลังเพื่อดึงเพลย์ลิสต์วิดีโอ DevBytes ล่วงหน้าจากเครือข่าย
- กําหนดเวลาให้ผู้ปฏิบัติงานทํางานเป็นระยะๆ
- เพิ่มข้อจํากัดใน
WorkRequest
- ตั้งเวลา
WorkRequest
เป็นระยะๆ ซึ่งดําเนินการวันละครั้ง
ใน Codelab นี้ คุณจะใช้แอป DevBytes ที่คุณพัฒนาใน Codelab ก่อนหน้านี้ได้ (หากไม่มีแอปนี้ คุณสามารถดาวน์โหลดรหัสเริ่มต้นสําหรับบทเรียนนี้)
แอป DevBytes จะแสดงรายการวิดีโอ DevByte ซึ่งเป็นบทแนะนําสั้นๆ จากทีมนักพัฒนาซอฟต์แวร์ Android ของ Google วิดีโอเหล่านี้นําเสนอฟีเจอร์สําหรับนักพัฒนาซอฟต์แวร์และแนวทางปฏิบัติแนะนําในการพัฒนา Android
คุณยกระดับประสบการณ์ของผู้ใช้ในแอปได้โดยดึงข้อมูลวิดีโอล่วงหน้าวันละครั้ง ซึ่งจะช่วยให้ผู้ใช้ได้รับเนื้อหาใหม่ๆ ทันทีที่เปิดแอป
ในงานนี้ คุณจะดาวน์โหลดและตรวจสอบโค้ดเริ่มต้น
ขั้นตอนที่ 1: ดาวน์โหลดและเรียกใช้แอปเริ่มต้น
คุณยังคงทํางานผ่านแอป DevBytes ที่สร้างไว้ใน Codelab ก่อนหน้านี้ได้ (หากมี) หรือคุณจะดาวน์โหลดแอปเริ่มต้นก็ได้
ในงานนี้ คุณจะดาวน์โหลดและเรียกใช้แอปเริ่มต้นและตรวจสอบโค้ดเริ่มต้นได้
- หากยังไม่มีแอป DevBytes โปรดดาวน์โหลดโค้ดเริ่มต้น DevBytes สําหรับ Codelab นี้จากโปรเจ็กต์ DevBytesRepository จาก GitHub
- แตกโค้ดแล้วเปิดโครงการใน Android Studio
- เชื่อมต่ออุปกรณ์ทดสอบหรือโปรแกรมจําลองกับอินเทอร์เน็ตหากยังไม่ได้เชื่อมต่อ สร้างและเรียกใช้แอป แอปจะดึงข้อมูลรายการวิดีโอ DevByte จากเครือข่ายและแสดง
- แตะวิดีโอในแอปเพื่อเปิดวิดีโอในแอป YouTube
ขั้นตอนที่ 2: สํารวจโค้ด
แอปเริ่มต้นมาพร้อมกับโค้ดจํานวนมากที่เปิดตัวใน Codelab ก่อนหน้านี้ โค้ดเริ่มต้นสําหรับ Codelab นี้มีเครือข่าย อินเทอร์เฟซผู้ใช้ แคชออฟไลน์ และโมดูลที่เก็บ คุณมุ่งเน้นที่การกําหนดเวลางานในเบื้องหลังได้โดยใช้ WorkManager
- ใน Android Studio ให้ขยายแพ็กเกจทั้งหมด
- สํารวจแพ็กเกจ
database
แพ็กเกจนี้มีเอนทิตีฐานข้อมูลและฐานข้อมูลภายใน ซึ่งมีการใช้งานโดยใช้Room
- สํารวจแพ็กเกจ
repository
แพ็กเกจนี้ประกอบด้วยคลาสVideosRepository
ที่แสดงนามชั้นข้อมูลจากส่วนที่เหลือของแอป - สํารวจโค้ดเริ่มต้นที่เหลือด้วยตัวเองและรับความช่วยเหลือจาก Codelab ก่อนหน้า
WorkManager
เป็นหนึ่งใน Android Architecture Components และเป็นส่วนหนึ่งของ Android Jetpack WorkManager
มีไว้สําหรับการทํางานในเบื้องหลังที่หน่วงเวลาได้และต้องมีการดําเนินการที่รับประกันการแสดงผล
- เลื่อนได้หมายความว่าไม่จําเป็นต้องทํางานทันที เช่น การส่งข้อมูลวิเคราะห์ไปยังเซิร์ฟเวอร์หรือซิงค์ฐานข้อมูลในเบื้องหลังเป็นข้อมูลที่เลื่อนได้
- การดําเนินการที่รับประกันการแสดงผลหมายความว่างานจะทํางานแม้ว่าแอปออกหรืออุปกรณ์รีสตาร์ท
แม้ว่า WorkManager
จะทํางานในเบื้องหลัง ระบบนี้ก็แก้ปัญหาความเข้ากันได้และแนวทางปฏิบัติแนะนําเกี่ยวกับแบตเตอรี่และความปลอดภัยของระบบ WorkManager
มีความเข้ากันได้กลับไปเป็น API ระดับ 14 WorkManager
เลือกวิธีที่เหมาะสมในการกําหนดเวลางานในเบื้องหลัง โดยขึ้นอยู่กับระดับ API ของอุปกรณ์ โดยอาจใช้ JobScheduler
(ใน API 23 ขึ้นไป) หรือใช้ AlarmManager
และ BroadcastReceiver
ร่วมกัน
WorkManager
ยังช่วยให้คุณกําหนดเกณฑ์และเวลาที่งานในเบื้องหลังจะทํางานได้อีกด้วย ตัวอย่างเช่น คุณอาจต้องการให้งานทํางานเฉพาะเมื่อสถานะแบตเตอรี่ สถานะเครือข่าย หรือสถานะการชาร์จตรงตามเกณฑ์บางอย่างเท่านั้น ดูวิธีตั้งข้อจํากัดใน Codelab นี้ได้ในภายหลัง
ใน Codelab นี้ คุณกําหนดเวลางานเพื่อดึงเพลย์ลิสต์วิดีโอ DevBytes ล่วงหน้าจากเครือข่ายวันละครั้ง หากต้องการกําหนดเวลางานนี้ คุณจะใช้ไลบรารี WorkManager
- เปิดไฟล์
build.gradle (Module:app)
และเพิ่มทรัพยากร Dependency ในWorkManager
ไปยังโปรเจ็กต์
หากคุณใช้ไลบรารี เวอร์ชันล่าสุด แอปโซลูชันควรรวบรวมตามที่คาดไว้ หากไม่ใช่ ให้ลองแก้ปัญหาหรือเปลี่ยนกลับเป็นเวอร์ชันไลบรารีที่แสดงด้านล่าง
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
- ซิงค์โปรเจ็กต์และตรวจสอบว่าไม่มีข้อผิดพลาดในการคอมไพล์
ทําความคุ้นเคยกับคลาสต่อไปนี้ในไลบรารี WorkManager
ก่อนเพิ่มโค้ดลงในโปรเจ็กต์
Worker
ชั้นเรียนนี้คือที่ที่คุณกําหนดงานจริง (งาน) ให้ทํางานในเบื้องหลัง คุณจะขยายคลาสนี้และลบล้างเมธอดdoWork()
ได้ วิธีdoWork()
คือที่ที่คุณนําโค้ดไปใช้ในเบื้องหลัง เช่น การซิงค์ข้อมูลกับเซิร์ฟเวอร์หรือการประมวลผลรูปภาพ คุณนําWorker
มาใช้ในงานนี้WorkRequest
ชั้นเรียนนี้แสดงคําขอเพื่อเรียกใช้ผู้ปฏิบัติงานในเบื้องหลัง ใช้WorkRequest
เพื่อกําหนดค่าวิธีการและเวลาที่จะมอบหมายงานของผู้ปฏิบัติงาน โดยรับความช่วยเหลือจากConstraints
เช่น ที่เสียบปลั๊กหรือเชื่อมต่อ Wi-Fi โดยคุณจะใช้WorkRequest
ในงานถัดไปWorkManager
ชั้นเรียนนี้จะกําหนดเวลาและเรียกใช้WorkRequest
ของคุณWorkManager
กําหนดเวลาคําของานในลักษณะที่กระจายภาระทรัพยากรของระบบ พร้อมทั้งทําตามข้อจํากัดที่คุณระบุไว้ โดยคุณจะใช้WorkManager
ในงานถัดไป
ขั้นตอนที่ 1: สร้างผู้ปฏิบัติงาน
ในงานนี้ คุณจะได้เพิ่ม Worker
เพื่อดึงเพลย์ลิสต์วิดีโอ DevBytes ไว้ล่วงหน้า
- ในแพ็กเกจ
devbyteviewer
ให้สร้างแพ็กเกจใหม่ที่ชื่อว่าwork
- ในแพ็กเกจ
work
ให้สร้างคลาส Kotlin ใหม่ที่ชื่อว่าRefreshDataWorker
- ขยายคลาส
RefreshDataWorker
จากชั้นเรียนCoroutineWorker
ส่งในcontext
และWorkerParameters
เป็นพารามิเตอร์เครื่องมือสร้าง
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
}
- หากต้องการแก้ไขข้อผิดพลาดคลาสนามธรรม ให้ลบล้างเมธอด
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
ให้สร้างและสร้างอินสแตนซ์ออบเจ็กต์VideosDatabase
และออบเจ็กต์VideosRepository
ภายในdoWork()
override suspend fun doWork(): Result {
val database = getDatabase(applicationContext)
val repository = VideosRepository(database)
return Result.success()
}
- ในคลาส
RefreshDataWorker
ภายในdoWork()
เหนือคําสั่งreturn
ให้เรียกใช้เมธอดrefreshVideos()
ภายในบล็อกtry
เพิ่มบันทึกเพื่อติดตามเมื่อผู้ปฏิบัติงานทํางาน
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
เพื่อเรียกใช้ผู้ปฏิบัติงานที่คุณสร้างไว้ในงานก่อนหน้า
ขั้นตอนที่ 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 รายการทํางาน หากการดําเนินการซิงค์ 1 รายการรอดําเนินการอยู่ คุณสามารถเลือกอนุญาตให้เรียกใช้หรือแทนที่ด้วยงานใหม่ได้โดยใช้ existingPeriodicWorkPolicy
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีนัดหมายเวลา WorkRequest
ในเอกสารประกอบเกี่ยวกับ WorkManager
- ในชั้นเรียน
RefreshDataWorker
เพิ่มออบเจ็กต์ที่แสดงร่วมกันเมื่อเริ่มต้นชั้นเรียน กําหนดชื่องานเพื่อระบุผู้ปฏิบัติงานคนนี้ไม่ซ้ํากัน
companion object {
const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
- ในชั้นเรียน
DevByteApplication
ที่ท้ายวิธีsetupRecurringWork()
ให้กําหนดเวลางานโดยใช้เมธอดenqueueUniquePeriodicWork()
ส่งในรูปแบบ enum ของKEEP
สําหรับที่มีอยู่เดิม ส่งในrepeatingRequest
เป็นพารามิเตอร์PeriodicWorkRequest
WorkManager.getInstance().enqueueUniquePeriodicWork(
RefreshDataWorker.WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
repeatingRequest)
หากมีงานที่รอดําเนินการ (ยังไม่เสร็จ) ที่มีชื่อเดียวกัน พารามิเตอร์ ExistingPeriodicWorkPolicy.
KEEP
จะทําให้ WorkManager
เก็บงานก่อนหน้าก่อนหน้านี้ไว้และทิ้งคําของานใหม่ไป
- ในตอนต้นของคลาส
DevByteApplication
ให้สร้างออบเจ็กต์CoroutineScope
ส่งใน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
ให้เพิ่มการเรียกไปยังเมธอดdelayedInit()
ที่ท้ายวิธีonCreate()
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
ให้แสดงความคิดเห็นของคําจํากัดความrepeatingRequest
ปัจจุบันในเมธอดsetupRecurringWork()
เพิ่มคําขอทํางานใหม่โดยตั้งช่วงเวลาซ้ําเป็น15
นาที
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
// .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
.build()
- เปิดแผง Logcat ใน Android Studio และกรองบน
RefreshDataWorker
หากต้องการล้างบันทึกก่อนหน้า ให้คลิกไอคอนล้าง Logcat - เรียกใช้แอป และ
WorkManager
กําหนดเวลาทํางานที่เกิดซ้ําทันที ในแผงบันทึก ให้สังเกตบันทึกที่ระบบจะเรียกใช้งานทุกๆ 15 นาที รอ 15 นาทีเพื่อดูบันทึกคําขอทํางานอีกชุด คุณสามารถปล่อยให้แอปทํางานหรือปิดแอปก็ได้ ผู้จัดการงานควรยังคงทํางานอยู่
ให้สังเกตว่าระยะเวลาน้อยกว่า 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 คุณจะระบุนโยบาย Backoff สําหรับการลองงานอีกครั้งก็ได้ ข้อจํากัดที่รองรับคือวิธีการที่ตั้งไว้ใน Constraints.Builder
ดูข้อมูลเพิ่มเติมได้ที่การกําหนดคําขอสําหรับที่ทํางาน
ขั้นตอนที่ 1: เพิ่มออบเจ็กต์ข้อจํากัดและกําหนดข้อจํากัด 1 รายการ
ในขั้นตอนนี้ คุณจะสร้างออบเจ็กต์ Constraints
และตั้งค่าข้อจํากัด 1 รายการบนออบเจ็กต์ดังกล่าว ซึ่งเป็นข้อจํากัดประเภทเครือข่าย (คุณจะเห็นบันทึกที่มีข้อจํากัดเพียงจุดเดียวได้ง่ายขึ้น ขั้นตอนต่อไปคือเพิ่มข้อจํากัดอื่นๆ)
- ในชั้นเรียน
DevByteApplication
ต้นsetupRecurringWork()
ให้กําหนดval
ของประเภทConstraints
ใช้เมธอดConstraints.Builder()
val constraints = Constraints.Builder()
หากต้องการแก้ไขข้อผิดพลาด ให้นําเข้า androidx.work.Constraints
- ใช้วิธีการ
setRequiredNetworkType()
เพื่อเพิ่มข้อจํากัดประเภทเครือข่ายในออบเจ็กต์constraints
ใช้ EUNMETERED
enum เพื่อให้คําของานทํางานเฉพาะเมื่ออุปกรณ์อยู่ในเครือข่ายที่ไม่มีการวัดปริมาณอินเทอร์เน็ต
.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
จะกําหนดเวลางานในเบื้องหลังทันที เนื่องจากข้อจํากัดด้านเครือข่าย งานจึงไม่ทํางาน
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
- ในชั้นเรียน
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
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
- การจํากัดคําขอทํางาน
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 Fundamentals