[AAC] WorkManager - 3

dwjeongยท2023๋…„ 11์›” 22์ผ
0

์•ˆ๋“œ๋กœ์ด๋“œ

๋ชฉ๋ก ๋ณด๊ธฐ
21/28

๐Ÿ”Ž Chaining Work

WorkManager๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์ข…์† ์ž‘์—…๋“ค์„ ์ง€์ •ํ•˜๊ณ  ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•˜๋Š” ์ž‘์—… ์ฒด์ธ์„ ์ƒ์„ฑํ•˜๊ณ  ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ. ํŠน์ • ์ˆœ์„œ๋กœ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์‹คํ–‰ํ•ด์•ผ ํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•จ.

์ž‘์—… ์ฒด์ธ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด WorkContinuation์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” WorkManager.beginWith(OneTimeWorkRequest) ๋˜๋Š” WorkManager.beginWith(List<OneTimeWorkRequest>) ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

WorkContinuation์˜ then(OneTimeWorkRequest) ๋˜๋Š” then(List<OneTimeWorkRequest>)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ข…์† OneTimeWorkRequest ์ธ์Šคํ„ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ.

WorkContinuation.then(...)์„ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค WorkContinuation์˜ ์ƒˆ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฐ˜ํ™˜๋จ.
OneTimeWorkRequest ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ด๋Ÿฌํ•œ ์š”์ฒญ์ด ์ž ์žฌ์ ์œผ๋กœ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ.

๋งˆ์ง€๋ง‰์œผ๋กœ WorkContinuation.enqueue() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WorkContinuation ์ฒด์ธ์„ enqueue()ํ•  ์ˆ˜ ์žˆ์Œ.


WorkManager.getInstance(myContext)
   // Candidates to run in parallel
   .beginWith(listOf(plantName1, plantName2, plantName3))
   // Dependent work (only runs after all previous work in chain)
   .then(cache)
   .then(upload)
   // Call enqueue to kick things off
   .enqueue()



๐Ÿ“– Input Mergers

OneTimeWorkRequest ์ธ์Šคํ„ด์Šค๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ์ƒ์œ„ ์ž‘์—… ์š”์ฒญ์˜ ์ถœ๋ ฅ์ด ํ•˜์œ„ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌ๋จ. ๋”ฐ๋ผ์„œ ์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” platName1, plantName2, plantName3์˜ ์ถœ๋ ฅ์ด ์บ์‹œ ์š”์ฒญ์— ๋Œ€ํ•œ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌ๋จ.

  • InputMerger๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 
    ์—ฌ๋Ÿฌ ์ƒ์œ„ ์ž‘์—… ์š”์ฒญ์˜ ์ž…๋ ฅ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด WorkManager๋Š” InputMerger๋ฅผ ์‚ฌ์šฉ.



๐Ÿ“• OverwritingInputMerger

OverwritingInputMerger๋Š” ๊ธฐ๋ณธ ๋ณ‘ํ•ฉ ๋ฐฉ๋ฒ•. ๋ณ‘ํ•ฉ์— ํ‚ค ์ถฉ๋Œ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ํ‚ค์˜ ์ตœ์‹  ๊ฐ’์ด ๊ฒฐ๊ณผ ์ถœ๋ ฅ ๋ฐ์ดํ„ฐ์˜ ์ด์ „ ๋ฒ„์ „์„ ๋ฎ์–ด์”€.

*์ถฉ๋Œ์ด ์ผ์–ด๋‚˜์ง€ ์•Š์•˜์„ ๋•Œ


  • ์ถฉ๋Œ์ด ์ผ์–ด๋‚ฌ์„ ๋•Œ

**์ž‘์—… ์š”์ฒญ์ด ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜์ง€ ์•Š์Œ.** ์œ„์˜ ์˜ˆ์—์„œ plantName1์€ ๋งˆ์ง€๋ง‰์— ๊ธฐ๋ก๋œ ๊ฐ’์— ๋”ฐ๋ผ "tulip" ๋˜๋Š” "elm" ๊ฐ’์„ ๋ณด์œ ํ•  ์ˆ˜ ์žˆ์Œ. ํ‚ค ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๊ณ  merger์˜ ๋ชจ๋“  ์ถœ๋ ฅ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ ArrayCreatingInputMerger๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ์ˆ˜ ์žˆ์Œ.

๐Ÿ“• ArrayCreatingInputMerger

์œ„์˜ ์˜ˆ์—์„œ ๋ชจ๋“  plant์˜ ์ด๋ฆ„ ์ถœ๋ ฅ์„ ๋ณด์กดํ•˜๋ ค๋ฉด ArrayCreatingInputMerger๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ.


val cache: OneTimeWorkRequest = OneTimeWorkRequestBuilder<PlantWorker>()
   .setInputMerger(ArrayCreatingInputMerger::class)
   .setConstraints(constraints)
   .build()

ArrayCreatingInputMerger๋Š” ๊ฐ ํ‚ค๋ฅผ ๋ฐฐ์—ด๊ณผ ์Œ์œผ๋กœ ์—ฐ๊ฒฐํ•จ.

์ถฉ๋Œ์ด ์ผ์–ด๋‚ฌ์„ ๊ฒฝ์šฐ



๐Ÿ“– ์ฒด์ด๋‹๊ณผ ์ž‘์—… ์ƒํƒœ

OneTimeWorkRequest ์ฒด์ธ์€ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด, ์ฆ‰ Result.success()๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋จ. ์ž‘์—… ์š”์ฒญ์€ ์‹คํ–‰ ์ค‘์— ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œ๋  ์ˆ˜ ์žˆ์–ด์„œ ์ข…์† ์ž‘์—… ์š”์ฒญ์— ํ›„์† ์˜ํ–ฅ์„ ๋ฏธ์นจ.

์ฒซ ๋ฒˆ์งธ OneTimeWorkRequest๊ฐ€ ์ž‘์—… ์š”์ฒญ ์ฒด์ธ์—์„œ ํ์— ์ถ”๊ฐ€๋˜๋ฉด ์ฒซ ๋ฒˆ์งธ ์ž‘์—… ์š”์ฒญ์˜ ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ชจ๋“  ํ›„์† ์ž‘์—… ์š”์ฒญ์ด ์ฐจ๋‹จ๋จ.


ํ์— ์ถ”๊ฐ€๋˜๊ณ  ๋ชจ๋“  ์ž‘์—… ์ œ์•ฝ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜๋ฉด ์ฒซ ๋ฒˆ์งธ ์ž‘์—… ์š”์ฒญ์ด ์‹คํ–‰๋˜๊ธฐ ์‹œ์ž‘ํ•จ.
์ž‘์—…์ด ๋ฃจํŠธ OneTimeWorkRequest ๋˜๋Š” List<OneTimeWorkRequest>์—์„œ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด, ์ฆ‰ Result.success()๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‹ค์Œ ์ข…์† ์ž‘์—… ์š”์ฒญ ์„ธํŠธ๊ฐ€ ํ์— ์ถ”๊ฐ€๋จ.


๊ฐ ์ž‘์—… ์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋œ๋‹ค๋ฉด ์ฒด์ธ์˜ ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ž‘์—… ์š”์ฒญ ์ฒด์ธ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„ ์ „์ฒด์— ์ด์™€ ๋™์ผํ•œ ํŒจํ„ด์ด ์ ์šฉ๋จ.


์ž‘์—…์ž๊ฐ€ ์ž‘์—… ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐœ๋ฐœ์ž๊ฐ€ ์ •์˜ํ•œ ๋ฐฑ์˜คํ”„ ์ •์ฑ…์— ๋”ฐ๋ผ ์š”์ฒญ์„ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Œ. ์ฒด์ธ์— ์†ํ•œ ์š”์ฒญ์„ ์žฌ์‹œ๋„ํ•˜๋ฉด ์ œ๊ณต๋œ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋กœ ํ•ด๋‹น ์š”์ฒญ๋งŒ ์žฌ์‹œ๋„๋จ. ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๋ชจ๋“  ์ž‘์—…์€ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Œ.



์žฌ์‹œ๋„ ์ •์ฑ…์ด ์ •์˜๋˜์–ด ์žˆ์ง€ ์•Š๊ฑฐ๋‚˜ ์—†๋Š” ๊ฒฝ์šฐ ๋˜๋Š” OneTimeWorkRequest๊ฐ€ Result.failure()๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒํƒœ์— ๋„๋‹ฌํ•˜๋ฉด ์ž‘์—… ์š”์ฒญ๊ณผ ๋ชจ๋“  ์ข…์† ์ž‘์—… ์š”์ฒญ์ด FAILED๋กœ ํ‘œ์‹œ๋จ


OneTimeWorkRequest๊ฐ€ ์ทจ์†Œ๋˜๋ฉด ๋™์ผํ•œ ๋กœ์ง์ด ์ ์šฉ๋จ. ์ข…์† ์ž‘์—… ์š”์ฒญ๋„ CANCELLED๋กœ ํ‘œ์‹œ๋˜๊ณ  ์ž‘์—…์ด ์‹คํ–‰๋˜์ง€ ์•Š์Œ.
**์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œ๋œ ์ž‘์—… ์š”์ฒญ์ด ์žˆ๋Š” ์ฒด์ธ์— ๋” ๋งŽ์€ ์ž‘์—… ์š”์ฒญ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ์ž‘์—… ์š”์ฒญ๋„ ๊ฐ๊ฐ FAILED ๋˜๋Š” CANCELLED๋กœ ํ‘œ์‹œ๋จ.** ๊ธฐ์กด ์ฒด์ธ์˜ ์ž‘์—…์„ ํ™•์žฅํ•˜๋ ค๋ฉด ExistingWorkPolicy์˜ APPEND_OR_REPLACE๋ฅผ ์ฐธ๊ณ .




๐Ÿ”Ž ์žฅ๊ธฐ ์‹คํ–‰ worker ์ง€์›

WorkManager์—๋Š” ์žฅ๊ธฐ ์‹คํ–‰ Worker์— ๋Œ€ํ•œ ์ง€์›์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Œ. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ WorkManager๋Š” ์ด ์ž‘์—…์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ๊ฐ€๋Šฅํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™œ์„ฑ ์ƒํƒœ๋กœ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์‹ ํ˜ธ๋ฅผ OS์— ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Œ. ์ด๋Ÿฌํ•œ Worker๋Š” 10๋ถ„ ์ด์ƒ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ. ์ด๊ฒƒ์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€๋กœ๋Š” ์ผ๊ด„ ์—…๋กœ๋“œ ๋˜๋Š” ์ผ๊ด„ ๋‹ค์šด๋กœ๋“œ(์ฒญํฌ ๋ถˆ๊ฐ€), ML ๋ชจ๋ธ์˜ ๋กœ์ปฌ ํฌ๋Ÿฐ์นญ, ์•ฑ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค‘์š”ํ•œ ์ž‘์—… ๋“ฑ์ด ์žˆ์Œ.

๋‚ด๋ถ€์ ์œผ๋กœ WorkManager๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ๋Œ€์‹ ํ•ด์„œ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์‹คํ–‰ํ•˜์—ฌ WorkRequest๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ์•Œ๋ฆผ๋„ ํ‘œ์‹œํ•จ.

๐Ÿ“– ์žฅ๊ธฐ ์‹คํ–‰ worker ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ

์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ์ฝ”๋”ฉํ•  ๋•Œ์™€ ์ž๋ฐ”๋กœ ์ฝ”๋”ฉํ•  ๋•Œ ๋‹ค๋ฅธ ์ ‘๊ทผ๋ฒ•์„ ์‚ฌ์šฉ.

  1. Kotlin
    Kotlin ๊ฐœ๋ฐœ์ž๋Š” CoroutineWorker๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•จ. setForegroundAsync()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ์ด ๋ฉ”์„œ๋“œ์˜ ์ •์ง€ ๋ฒ„์ „์ธ setForeground()๋ฅผ ์‚ฌ์šฉ.
class DownloadWorker(context: Context, parameters: WorkerParameters) :
   CoroutineWorker(context, parameters) {

   private val notificationManager =
       context.getSystemService(Context.NOTIFICATION_SERVICE) as
               NotificationManager

   override suspend fun doWork(): Result {
       val inputUrl = inputData.getString(KEY_INPUT_URL)
                      ?: return Result.failure()
       val outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME)
                      ?: return Result.failure()
       // Mark the Worker as important
       val progress = "Starting Download"
       setForeground(createForegroundInfo(progress))
       download(inputUrl, outputFile)
       return Result.success()
   }

   private fun download(inputUrl: String, outputFile: String) {
       // Downloads a file and updates bytes read
       // Calls setForeground() periodically when it needs to update
       // the ongoing Notification
   }
   // Creates an instance of ForegroundInfo which can be used to update the
   // ongoing notification.
   private fun createForegroundInfo(progress: String): ForegroundInfo {
       val id = applicationContext.getString(R.string.notification_channel_id)
       val title = applicationContext.getString(R.string.notification_title)
       val cancel = applicationContext.getString(R.string.cancel_download)
       // This PendingIntent can be used to cancel the worker
       val intent = WorkManager.getInstance(applicationContext)
               .createCancelPendingIntent(getId())

       // Create a Notification channel if necessary
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           createChannel()
       }

       val notification = NotificationCompat.Builder(applicationContext, id)
           .setContentTitle(title)
           .setTicker(title)
           .setContentText(progress)
           .setSmallIcon(R.drawable.ic_work_notification)
           .setOngoing(true)
           // Add the cancel action to the notification which can
           // be used to cancel the worker
           .addAction(android.R.drawable.ic_delete, cancel, intent)
           .build()

       return ForegroundInfo(notificationId, notification)
   }

   @RequiresApi(Build.VERSION_CODES.O)
   private fun createChannel() {
       // Create a Notification channel
   }

   companion object {
       const val KEY_INPUT_URL = "KEY_INPUT_URL"
       const val KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME"
   }
}

  1. Java
    ListenableWorker ๋˜๋Š” Worker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋Š” ListenableFuture<Void>๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” setForegroundAsync() API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ. ๋˜ํ•œ ์ง„ํ–‰ ์ค‘์ธ Notification์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด setForegroundAsync()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ์Œ.
public class DownloadWorker extends Worker {
   private static final String KEY_INPUT_URL = "KEY_INPUT_URL";
   private static final String KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME";

   private NotificationManager notificationManager;

   public DownloadWorker(
       @NonNull Context context,
       @NonNull WorkerParameters parameters) {
           super(context, parameters);
           notificationManager = (NotificationManager)
               context.getSystemService(NOTIFICATION_SERVICE);
   }

   @NonNull
   @Override
   public Result doWork() {
       Data inputData = getInputData();
       String inputUrl = inputData.getString(KEY_INPUT_URL);
       String outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME);
       // Mark the Worker as important
       String progress = "Starting Download";
       setForegroundAsync(createForegroundInfo(progress));
       download(inputUrl, outputFile);
       return Result.success();
   }

   private void download(String inputUrl, String outputFile) {
       // Downloads a file and updates bytes read
       // Calls setForegroundAsync(createForegroundInfo(myProgress))
       // periodically when it needs to update the ongoing Notification.
   }

   @NonNull
   private ForegroundInfo createForegroundInfo(@NonNull String progress) {
       // Build a notification using bytesRead and contentLength

       Context context = getApplicationContext();
       String id = context.getString(R.string.notification_channel_id);
       String title = context.getString(R.string.notification_title);
       String cancel = context.getString(R.string.cancel_download);
       // This PendingIntent can be used to cancel the worker
       PendingIntent intent = WorkManager.getInstance(context)
               .createCancelPendingIntent(getId());

       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           createChannel();
       }

       Notification notification = new NotificationCompat.Builder(context, id)
               .setContentTitle(title)
               .setTicker(title)
               .setSmallIcon(R.drawable.ic_work_notification)
               .setOngoing(true)
               // Add the cancel action to the notification which can
               // be used to cancel the worker
               .addAction(android.R.drawable.ic_delete, cancel, intent)
               .build();

       return new ForegroundInfo(notificationId, notification);
   }

   @RequiresApi(Build.VERSION_CODES.O)
   private void createChannel() {
       // Create a Notification channel
   }
}

๐Ÿ“– ์žฅ๊ธฐ ์‹คํ–‰ worker์— ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜• ์ถ”๊ฐ€

โญ ์ฐธ๊ณ : ์•ฑ์ด ํƒ€๊ฒŸํŒ…ํ•˜๋Š” API ์ˆ˜์ค€๊ณผ ์„œ๋น„์Šค๊ฐ€ ์‹คํ–‰ํ•˜๋Š” ์ž‘์—…์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์„ ์–ธํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Œ. ํƒ€๊ฒŸํŒ…ํ•˜๋Š” Android ๋ฒ„์ „๊ณผ ๊ด€๊ณ„์—†์ด ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ.

์•ฑ์ด Android 14 (API ์ˆ˜์ค€ 34) ์ด์ƒ์„ ํƒ€๊ฒŸํŒ…ํ•˜๋Š” ๊ฒฝ์šฐ ๋ชจ๋“  ์žฅ๊ธฐ ์‹คํ–‰ worker์˜ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์ง€์ •ํ•ด์•ผํ•จ.

์•ฑ์—์„œ Android 10 (API ์ˆ˜์ค€ 29) ์ด์ƒ์„ ํƒ€๊ฒŸํŒ…ํ•˜๊ณ  ์œ„์น˜์— ์•ก์„ธ์Šคํ•ด์•ผ ํ•˜๋Š” ์žฅ๊ธฐ ์‹คํ–‰ worker๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ worker๊ฐ€ location์˜ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์‚ฌ์šฉํ•จ์„ ๋‚˜ํƒ€๋ƒ„.

์•ฑ์ด Android 11 (API ์ˆ˜์ค€ 30) ์ด์ƒ์„ ํƒ€๊ฒŸํŒ…ํ•˜๊ณ  ์นด๋ฉ”๋ผ ๋˜๋Š” ๋งˆ์ดํฌ์— ์•ก์„ธ์Šคํ•ด์•ผ ํ•˜๋Š” ์žฅ๊ธฐ ์‹คํ–‰ worker๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ๊ฐ camera ๋˜๋Š” microphone ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์„ ์–ธ.


์•ฑ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์—์„œ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜• ์„ ์–ธ

์•ฑ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์—์„œ worker์˜ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์„ ์–ธ.

<service
   android:name="androidx.work.impl.foreground.SystemForegroundService"
   android:foregroundServiceType="location|microphone"
   tools:node="merge" />

๋Ÿฐํƒ€์ž„ ์‹œ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜• ์ง€์ •

setForeground() ๋˜๋Š” setForegroundAsync()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์œ ํ˜•์„ ์ง€์ •.

โญ ์ฐธ๊ณ : Android 14 (API ์ˆ˜์ค€ 34)๋ถ€ํ„ฐ setForeground() ๋˜๋Š” setForegroundAsync()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์‹œ์Šคํ…œ์—์„œ ์„œ๋น„์Šค ์œ ํ˜•์— ๋”ฐ๋ผ ํŠน์ • ๊ธฐ๋ณธ ์š”๊ฑด์„ ํ™•์ธ

private fun createForegroundInfo(progress: String): ForegroundInfo {
   // ...
   return ForegroundInfo(NOTIFICATION_ID, notification,
           FOREGROUND_SERVICE_TYPE_LOCATION or
FOREGROUND_SERVICE_TYPE_MICROPHONE) }




Worker ์ค‘๊ฐ„ ์ง„ํ–‰๋ฅ  ๊ด€์ฐฐ

WorkManager๋Š” Worker์˜ ์ค‘๊ฐ„ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์„ค์ •ํ•˜๊ณ  ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์› ์ œ๊ณต.
์•ฑ์ด ํฌ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋Š” ๋™์•ˆ Worker๊ฐ€ ์‹คํ–‰์ค‘์ธ ๊ฒฝ์šฐ WorkInfo์˜ LiveData๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ •๋ณด๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Œ.

ListenableWorker๋Š” ์ค‘๊ฐ„ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” setProgressAsync() API๋ฅผ ์ง€์›.
์ด API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ์ž๋Š” UI์—์„œ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘๊ฐ„ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ.
์ง„ํ–‰ ์ƒํ™ฉ์€ ์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•œ ์†์„ฑ ์ปจํ…Œ์ด๋„ˆ์ธ ๋ฐ์ดํ„ฐ ์œ ํ˜•์œผ๋กœ ํ‘œ์‹œ๋จ.


์ง„ํ–‰ ์ •๋ณด๋Š” ListenableWorker๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ์—๋งŒ ๊ด€์ฐฐํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ.
ListenableWorker์˜ ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ํ›„ ์ง„ํ–‰๋ฅ ์„ ์„ค์ •ํ•˜๋ ค๋Š” ์‹œ๋„๋Š” ๋ฌด์‹œ๋จ.

getWorkInfoByโ€ฆ() ๋˜๋Š” getWorkInfoByโ€ฆLiveData() ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง„ํ–‰ ์ƒํ™ฉ ์ •๋ณด๋ฅผ ๊ด€์ฐฐํ•  ์ˆ˜๋„ ์žˆ์Œ.

์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ๋Š” Data๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒˆ๋กœ์šด getProgress() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” WorkInfo์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•จ.


๐Ÿ“– Update Progress

ListenableWorker ๋˜๋Š” Worker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Java ๊ฐœ๋ฐœ์ž์˜ ๊ฒฝ์šฐ setProgressAsync() API๋Š” ListenableFuture<Void>๋ฅผ ๋ฐ˜ํ™˜ํ•จ.

์—…๋ฐ์ดํŠธ ํ”„๋กœ์„ธ์Šค์—๋Š” ์ง„ํ–‰๋ฅ  ์ •๋ณด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์ž‘์—…์ด ํฌํ•จ๋˜๋ฏ€๋กœ ์—…๋ฐ์ดํŠธ ์ง„ํ–‰๋ฅ ์€ ๋น„๋™๊ธฐ์‹.

Kotlin์—์„œ๋Š” CoroutineWorker ๊ฐ์ฒด์˜ setProgress() ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง„ํ–‰ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ.

//java
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ProgressWorker extends Worker {

    private static final String PROGRESS = "PROGRESS";
    private static final long DELAY = 1000L;

    public ProgressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
        super(context, parameters);
        // Set initial progress to 0
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 0).build());
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            // Doing work.
            Thread.sleep(DELAY);
        } catch (InterruptedException exception) {
            // ... handle exception
        }
        // Set progress to 100 after you are done doing your work.
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 100).build());
        return Result.success();
    }
}
//kotlin
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import kotlinx.coroutines.delay

class ProgressWorker(context: Context, parameters: WorkerParameters) :
    CoroutineWorker(context, parameters) {

    companion object {
        const val Progress = "Progress"
        private const val delayDuration = 1L
    }

    override suspend fun doWork(): Result {
        val firstUpdate = workDataOf(Progress to 0)
        val lastUpdate = workDataOf(Progress to 100)
        setProgress(firstUpdate)
        delay(delayDuration)
        setProgress(lastUpdate)
        return Result.success()
    }
}

๐Ÿ“– Observing Progress

์ง„ํ–‰ ์ •๋ณด๋ฅผ ๊ด€์ฐฐํ•˜๊ธฐ ์œ„ํ•ด getWorkInfoByโ€ฆ() ๋˜๋Š” getWorkInfoByโ€ฆLiveData() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  WorkInfo์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ.

//java
WorkManager.getInstance(getApplicationContext())
     // requestId is the WorkRequest id
     .getWorkInfoByIdLiveData(requestId)
     .observe(lifecycleOwner, new Observer<WorkInfo>() {
             @Override
             public void onChanged(@Nullable WorkInfo workInfo) {
                 if (workInfo != null) {
                     Data progress = workInfo.getProgress();
                     int value = progress.getInt(PROGRESS, 0)
                     // Do something with progress
             }
      }
});
//kotlin
WorkManager.getInstance(applicationContext)
    // requestId is the WorkRequest id
    .getWorkInfoByIdLiveData(requestId)
    .observe(observer, Observer { workInfo: WorkInfo? ->
            if (workInfo != null) {
                val progress = workInfo.progress
                val value = progress.getInt(Progress, 0)
                // Do something with progress information
            }
    })




๐Ÿ”Ž ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋œ ์ž‘์—… ์—…๋ฐ์ดํŠธ

WorkManager๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฏธ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•œ WorkRequest๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ.
์ œ์•ฝ ์กฐ๊ฑด์„ ์ž์ฃผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์ž‘์—…์ž๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋Š” ๋Œ€๊ทœ๋ชจ ์•ฑ์—์„œ ์ข…์ข… ํ•„์š”.
WorkManager ๋ฒ„์ „ 2.8.0๋ถ€ํ„ฐ๋Š” updateWork() API๊ฐ€ ์ด๋ฅผ ์ˆ˜ํ–‰.


updateWork() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ƒˆ ํ•ญ๋ชฉ์„ ์ˆ˜๋™์œผ๋กœ ์ทจ์†Œํ•˜๊ณ  ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ ๋„ ์ฆ‰์‹œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ.


๐Ÿ“– ์ž‘์—… ์ทจ์†Œ ํ”ผํ•˜๊ธฐ

์ผ๋ฐ˜์ ์œผ๋กœ ๊ธฐ์กด WorkRequest๋ฅผ ์ทจ์†Œํ•˜๊ณ  ์ƒˆ WorkRequest๋ฅผ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•ด์•ผํ•จ.
๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ฑ์ด ํŠน์ • ์ž‘์—…์„ ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ƒ๋‹นํ•œ ์–‘์˜ ์ถ”๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Œ.

โ— WorkRequest๋ฅผ ์ทจ์†Œํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์‹œ
1. Back-end request
์„œ๋ฒ„์— ๋ณด๋‚ผ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋™์•ˆ Worker๋ฅผ ์ทจ์†Œํ•˜๋ฉด ์ƒˆ Worker๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜์—ฌ ์ž ์žฌ์ ์œผ๋กœ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์•ผํ•จ.

2. Scheduling
PeriodicWorkRequest๋ฅผ ์ทจ์†Œํ•˜๊ณ  ์ƒˆ PeriodicWorkRequest๊ฐ€ ๋™์ผํ•œ ์ผ์ •์— ๋”ฐ๋ผ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋ ค๋ฉด ์ƒˆ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์ด์ „ ์ž‘์—… ์š”์ฒญ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์‹œ๊ฐ„ ์˜คํ”„์…‹์„ ๊ณ„์‚ฐํ•ด์•ผํ•จ.

updateWork() API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ƒˆ ์š”์ฒญ์„ ์ทจ์†Œํ•˜๊ณ  ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ ์—†์ด ์ž‘์—… ์š”์ฒญ์˜ ์ œ์•ฝ ์กฐ๊ฑด๊ณผ ๊ธฐํƒ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ.


์ž‘์—…์„ ์ทจ์†Œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

updateWork()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  WorkRequest๋ฅผ ์ง์ ‘ ์ทจ์†Œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Œ.
๐Ÿ‘‰ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•œ ์ž‘์—…์˜ ๊ทผ๋ณธ์  ํŠน์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์ˆ˜ํ–‰ํ•ด์•ผํ•˜๋Š” ์ž‘์—….

โ— ์ฃผ์˜: updateWork()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WorkRequest์˜ Worker ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ. ์˜ˆ๋ฅผ ๋“ค์–ด OneTimeWorkRequest๋ฅผ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•˜๊ณ  ์ •๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๋ ค๋ฉด ์š”์ฒญ์„ ์ทจ์†Œํ•˜๊ณ  ์ƒˆ PeriodicWorkRequest๋ฅผ ์˜ˆ์•ฝํ•ด์•ผํ•จ.


์ž‘์—…์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

์˜ˆ:
์‚ฌ์šฉ์ž์˜ ์‚ฌ์ง„์„ ๋งค์ผ ๋ฐฑ์—…ํ•˜๋Š” ์‚ฌ์ง„ ์•ฑ์ด ์žˆ์„ ๋•Œ, ์ด๋ฅผ ์œ„ํ•ด PeriodicWorkRequest๋ฅผ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€.
WorkRequest์—๋Š” ์žฅ์น˜๊ฐ€ ์ถฉ์ „ ์ค‘์ด๊ณ  Wi-Fi์— ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์ œ์•ฝ์ด ์žˆ์Œ.
์‚ฌ์šฉ์ž๋Š” ๊ธ‰์† ์ถฉ์ „๊ธฐ๋ฅผ ์ด์šฉํ•ด ํ•˜๋ฃจ์— 20๋ถ„๋งŒ ๊ธฐ๊ธฐ๋ฅผ ์ถฉ์ „. ์ด ๊ฒฝ์šฐ ์•ฑ์€ WorkRequest๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ถฉ์ „ ์ œํ•œ์„ ์™„ํ™”ํ•˜์—ฌ ๊ธฐ๊ธฐ๊ฐ€ ์™„์ „ํžˆ ์ถฉ์ „๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋„ ์‚ฌ์ง„์„ ๊ณ„์† ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Œ.

์ด ์ƒํ™ฉ์—์„œ๋Š” updateWork() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—… ์š”์ฒญ์˜ ์ œ์•ฝ ์กฐ๊ฑด์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ.



๐Ÿ“– ์ž‘์—… ์—…๋ฐ์ดํŠธ ๋ฐฉ๋ฒ•

updateWork() ๋ฉ”์„œ๋“œ๋Š” ์ƒˆ WorkRequest๋ฅผ ์ทจ์†Œํ•˜๊ณ  ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•  ํ•„์š” ์—†์ด ๊ธฐ์กด WorkRequest๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณต.

  • ์—…๋ฐ์ดํŠธ ๋Œ€๊ธฐ์—ด์— ํฌํ•จ๋œ ์ž‘์—…์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋‹จ๊ณ„

    1. ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋œ ์ž‘์—…์˜ ๊ธฐ์กด ID ๊ฐ€์ ธ์˜ค๊ธฐ
    ์—…๋ฐ์ดํŠธํ•˜๋ ค๋Š” WorkRequest์˜ ID๋ฅผ ๊ฐ€์ ธ์˜ด. getWorkInfo API๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋Œ€๊ธฐ์—ด์— ๋„ฃ๊ธฐ ์ „์— ๊ณต์šฉ ์†์„ฑ WorkRequest.id๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚˜์ค‘์— ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ดˆ๊ธฐ WorkRequest์˜ ID๋ฅผ ์ˆ˜๋™์œผ๋กœ ์œ ์ง€ํ•˜์—ฌ ์ด ID๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Œ.

    2. ์ƒˆ WorkRequest ๋งŒ๋“ค๊ธฐ
    ์ƒˆ WorkRequest๋ฅผ ๋งŒ๋“ค๊ณ  WorkRequest.Builder.setID()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์กด WorkRequest์˜ ID์™€ ์ผ์น˜ํ•˜๋„๋ก ID๋ฅผ ์„ค์ •

    3. ์ œ์•ฝ ์กฐ๊ฑด ์„ค์ •
    WorkRequest.Builder.setConstraints()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WorkManager์— ์ƒˆ ์ œ์•ฝ ์กฐ๊ฑด์„ ์ „๋‹ฌ.

    4.updateWork ํ˜ธ์ถœ
    ์ƒˆ WorkRequest๋ฅผ updateWork()์— ์ „๋‹ฌํ•จ.
  • ์˜ˆ์‹œ
    updateWork() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” WorkRequest์˜ ๋ฐฐํ„ฐ๋ฆฌ ์ œ์•ฝ ์กฐ๊ฑด์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ์ œ.
suspend fun updatePhotoUploadWork() {
    // Get instance of WorkManager.
    val workManager = WorkManager.getInstance(context)

    // Retrieve the work request ID. In this example, the work being updated is unique
    // work so we can retrieve the ID using the unique work name.
    val photoUploadWorkInfoList = workManager.getWorkInfosForUniqueWork(
        PHOTO_UPLOAD_WORK_NAME
    ).await()

    val existingWorkRequestId = photoUploadWorkInfoList.firstOrNull()?.id ?: return

    // Update the constraints of the WorkRequest to not require a charging device.
    val newConstraints = Constraints.Builder()
        // Add other constraints as required here.
        .setRequiresCharging(false)
        .build()

    // Create new WorkRequest from existing Worker, new constraints, and the id of the old WorkRequest.
    val updatedWorkRequest: WorkRequest =
        OneTimeWorkRequestBuilder<MyWorker>()
            .setConstraints(newConstraints)
            .setId(existingWorkRequestId)
            .build()

    // Pass the new WorkRequest to updateWork().
    workManager.updateWork(updatedWorkRequest)
}

  • ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌํ•˜๊ธฐ
    updateWork()๋Š” ListenableFuture<UpdateResult>๋ฅผ ๋ฐ˜ํ™˜ํ•จ.
    ์ง€์ •๋œ UpdateResult์—๋Š” WorkManager๊ฐ€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์„ค๋ช…ํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐ’ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ.
    ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ์‹œ๊ธฐ๋„ ํ‘œ์‹œ๋จ.



๐Ÿ“– ์ž‘์—… ์ถ”์ ํ•˜๊ธฐ

WorkRequest๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ๋งˆ๋‹ค generation์ด 1์”ฉ ์ฆ๊ฐ€. ์ด๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ํ์— ์ถ”๊ฐ€๋œ WorkRequest๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Œ.
generation๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž‘์—… ์š”์ฒญ์„ ๊ด€์ฐฐ, ์ถ”์ , ํ…Œ์ŠคํŠธํ•  ๋•Œ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Œ.

  • WorkRequest์˜ generation์„ ์–ป๊ธฐ ์œ„ํ•œ ๋‹จ๊ณ„
  1. WorkInfo
    WorkManager.getWorkInfoById()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ WorkRequest์— ํ•ด๋‹นํ•˜๋Š” WorkInfo ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฒ€์ƒ‰. WorkInfo๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ.

  2. getGeneration
    WorkInfo ์ธ์Šคํ„ด์Šค์—์„œ getGeneration()์„ ํ˜ธ์ถœ. ๋ฐ˜ํ™˜๋œ Int๋Š” WorkRequest generation์— ํ•ด๋‹น.
    generation์— ํ•„๋“œ๋‚˜ ์†์„ฑ์€ ์—†๊ณ  WorkInfo.getGeneration() ๋ฉ”์„œ๋“œ๋งŒ ์žˆ์Œ.


  • ์˜ˆ์‹œ
// Get instance of WorkManager.
val workManager = WorkManager.getInstance(context)

// Retrieve WorkInfo instance.
val workInfo = workManager.getWorkInfoById(oldWorkRequestId)

// Call getGeneration to retrieve the generation.
val generation = workInfo.getGeneration()

๐Ÿ“ ์ฐธ๊ณ : updateWork()๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” UpdateResult์—๋Š” WorkRequest ์ƒ์„ฑ์ด ํฌํ•จ๋˜์ง€ ์•Š์Œ.


๐Ÿ“– ์ž‘์—… ์—…๋ฐ์ดํŠธ ์ •์ฑ…

์ฃผ๊ธฐ์  ์ž‘์—… ์—…๋ฐ์ดํŠธ์— ๊ถŒ์žฅ๋˜๋Š” ์†”๋ฃจ์…˜์€ ExistingPeriodicWorkPolicy.REPLACE ์ •์ฑ…์„ ์‚ฌ์šฉํ•˜์—ฌ PeriodicWorkRequest๋ฅผ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ(๋™์ผํ•œ ๊ณ ์œ  ID๋ฅผ ๊ฐ€์ง„ ๋ณด๋ฅ˜ ์ค‘์ธ PeriodicWorkRequest๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ƒˆ WorkRequest๋Š” ์ด๋ฅผ ์ทจ์†Œํ•˜๊ณ  ์‚ญ์ œํ•จ)์ด์—ˆ์œผ๋‚˜

ExistingPeriodicWorkPolicy.UPDATE๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์›Œํฌํ”Œ๋กœ๋ฅผ ์œ„ํ•ด ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ.

PeriodicWorkRequest์™€ ํ•จ๊ป˜ enqueueUniquePeriodicWork๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ExistingPeriodicWorkPolicy.UPDATE ์ •์ฑ…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ PeriodicWorkRequest๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Œ.

๋™์ผํ•œ unique name์„ ๊ฐ€์ง„ ๋ณด๋ฅ˜ ์ค‘์ธ PeriodicWorkRequest๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ WorkManager๋Š” ์ด๋ฅผ ์ƒˆ ์‚ฌ์–‘์œผ๋กœ ์—…๋ฐ์ดํŠธํ•จ.

์ด ์›Œํฌํ”Œ๋กœ๋ฅผ ๋”ฐ๋ฅด๋ฉด updateWork()๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Œ.


WorkManger ์Šค๋ ˆ๋”ฉ ํŽ˜์ด์ง€

0๊ฐœ์˜ ๋Œ“๊ธ€