๊ตฌ๊ธ์์ ์ ๊ณตํ๋ ๊ต์ก์๋ฃ๋ฅผ ์ ๋ฆฌํ๊ธฐ ์ํ ํฌ์คํธ์ ๋๋ค.
ViewModel์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฒ๋ฆฌ๋ฅผ ์ํ DAO ๊ฐ์ฒด์ ์ง์ ์ฐธ์กฐ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
์ฑ์ด ๋คํธ์ํฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ ์ธ๋ถ ๋ฆฌ์์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ ๊ฒฝ์ฐ ViewModel์ ์ถ๊ฐํด์ผ ํ๋ฉฐ ์ถ๊ฐ์ ์ธ ํ์ฅ์ ์ด๋ ค์์ด ์์ต๋๋ค. ๋ํ ViewModel์ด ๋ณต์กํด์ง๋ฉฐ ์ง์ ๋ฐ์ดํฐ ๊ฒ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ์ํ์ง ์์ต๋๋ค.
์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์๋ ๋คํธ์ํฌ ์์ฒญ์ ์ํด ๋ก์ปฌ ์ ์ฅ์์ ๋คํธ์ํฌ ์๋ต ์บ์ฑ์ ๊ณ ๋ คํ ์ ์์ต๋๋ค.
Repository๋ data source์ ์ฑ ์ฌ์ด์ ์ถ์ํ ๊ณ์ธต์ ์ถ๊ฐํฉ๋๋ค. ์ด๋ ์ฑ ์ํคํ ์ฒ์์ ์ด์ผ๊ธฐํ ๊ด์ฌ์ฌ ๋ถ๋ฆฌ ์์น์ ๋ฐ๋ฆ ๋๋ค.
Repository๋ฅผ ์ถ๊ฐํ๋ฉด์ ์๋ก์ด data source๊ฐ ์ถ๊ฐ๋๋ ๊ฒฝ์ฐ ์ฑ์ ํ์ฅ์ฑ์ด ํฅ์๋ฉ๋๋ค. Repository๋ง ๋ณ๊ฒฝํ๋ฉด ๋๊ณ ๊ทธ ์์ ๋ค๋ฅธ ๊ตฌ์ฑ ์์(UI controller, ViewModel)๋ ๋๋ถ๋ถ ๋์ผํ๊ฒ ์ ์ง๋ฉ๋๋ค.
๋ํ data soucre๋ฅผ mock ํ์ฌ ํ ์คํธ๋ฅผ ์งํํ ์ ์์ต๋๋ค.
Immediate tasks : ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์ ๋๊ธฐํํ๋ ๊ฒ๊ณผ ๊ฐ์ด ์ฆ์ ์คํํด์ผ ํ๋ ์์
. ์ฌ์ฉ์๊ฐ ์ทจ์ํ ์ ์๋ ์์
์ ๊ฒฝ์ฐ Coroutines
์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
Deferred tasks : ์ฌ์ฉ์ ์ํธ ์์ฉ์ ์ง์ ์ฐ๊ฒฐ๋์ง ์๊ณ ๋์ค์ ์คํํ ์ ์๋ ๋ชจ๋ ์์
. WorkManager
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
Exact tasks : ์์
์ด ํน์ ๋ฏธ๋ ์๊ฐ์ ์คํ๋์ด์ผ ํ๋ ๊ฒฝ์ฐ AlarmManager
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
implementation "androidx.work:work-runtime-ktx:$work_version"
Worker : ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์์
์ ์ํํ๊ณ doWork()
๋ฉ์๋๋ฅผ ์ฌ์ ์ํฉ๋๋ค.
WorkRequest : ์์
์์ฒญ.
Constraint : ์์
์ ์คํํ ์ ์๋ ์กฐ๊ฑด.
WorkManager : WorkRequest๊ฐ ์คํ๋๋๋ก ์์ฝํฉ๋๋ค.
Worker ํด๋์ค๋ฅผ ์๋ธํด๋์ฑํ๊ณ doWork()
๋ฅผ ์ฌ์ ์ํฉ๋๋ค.
class UploadWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
// Do the work here. In this case, upload the images.
uploadImages()
// Indicate whether work finished successfully with the Result
return Result.success()
}
}
WorkManager๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฝ๋ฃจํด์ ์ง์ํฉ๋๋ค. CoroutineWorker๋ฅผ ์๋ธํด๋์ฑํ๊ณ doWork()
๋ฅผ ์ผ์ ์ค๋จ ํจ์๋ก ๊ตฌํํด์ผ ํฉ๋๋ค.
class UploadWorker(appContext: Context, workerParams: WorkerParameters) :
CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
// Do the work here (in this case, upload the images)
uploadImages()
// Indicate whether work finished successfully with the Result
return Result.success()
}
}
WorkRequest๋ฅผ ์์ฑํฉ๋๋ค.
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<UploadWorker>()
.build()
WorkManager queue์ ์์ ์ ๋ํด์ค๋๋ค.
WorkManager.getInstance(myContext)
.enqueue(uploadWorkRequest)
์๋ฅผ ๋ค์ด ์์
์์ฒญ์ด ํ๋ฃจ์ ํ ๋ฒ ์คํ๋๊ธฐ๋ฅผ ์ํ๋ค๊ณ ํ์ ๋ PeriodicWorkRequest
๋ฅผ ์ฌ์ฉํด ์ค์ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฒซ ๋ฒ์งธ๋ ์คํ 11์ 50๋ถ, ๋ค์๋ ์ค์ 12์ 10๋ถ์ ์์
์ ์คํํ ์ ์๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฐ๊ณผ๋ ์์
์์ฒญ์ ์๋๊ฐ ์๋ ์ ์์ต๋๋ค.
์์ ์์ฒญ์ด ๋๋ต ํ๋ฃจ ๊ฐ๊ฒฉ์ผ๋ก ์คํ๋๋๋ก ํ๋ ค๋ฉด ๊ฐ๋ณ ๊ฐ๊ฒฉ์ ์ฌ์ฉํฉ๋๋ค. ๊ฐ๋ณ ๊ธฐ๊ฐ์ repeatInterval - flexInterval์์ ์์ํ์ฌ ๊ฐ๊ฒฉ์ด ๋๋ ๋๊น์ง์ ๋๋ค. ํ๋ ์ค์ ๋ฐ๋ณต์ ํจ๊ป ์ฌ์ฉํ๋ฉด ๊ฐ์์ ์๋ฅผ ๋ค์ด ๋งค์ผ ๋ง์ง๋ง ์๊ฐ์ ์คํ๋๋๋ก ์์ฝ๋ ์์ ์ ๋ง๋ค ์ ์์ต๋๋ค.
๋ ๋ฒ์งธ ์๋ ํน์ ๊ฐ๋ณ ๊ฐ๊ฒฉ ๋ด์์ ์คํ๋๋๋ก ๋ฐ๋ณต ๊ฐ๊ฒฉ์ ์กฐ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค. ์ด ์๋๋ฆฌ์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฒญ์ด ์ฝ ํ๋ฃจ ๊ฐ๊ฒฉ์ผ๋ก ์คํ๋ ๊ฒ์ด๋ผ๊ณ ํ์ ํ ์ ์์ต๋๋ค.
๊ฐ๋ณ ๊ธฐ๊ฐ์ผ๋ก ์ฃผ๊ธฐ์ ์์ ์ ์ ์ํ๋ ค๋ฉด PeriodicWorkRequest๋ฅผ ์์ฑํ ๋ repeatInterval๊ณผ ํจ๊ป flexInterval์ ์ ๋ฌํฉ๋๋ค. ํ๋ ์ค ๊ธฐ๊ฐ์ repeatInterval - flexInterval์์ ์์ํ์ฌ ๊ฐ๊ฒฉ์ด ๋๋ ๋๊น์ง์ ๋๋ค.
๋ฐ๋ณต ๊ฐ๊ฒฉ์ PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS
๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์์ผ ํ๊ณ ๊ฐ๋ณ ๊ฐ๊ฒฉ์ PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS
๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์์ผ ํฉ๋๋ค.
์๋ ์์๋ ๋งค 1์๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋ง์ง๋ง 15๋ถ ๋์ ์คํ๋ฉ๋๋ค.
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(
1, TimeUnit.HOURS, // repeatInterval
15, TimeUnit.MINUTES // flexInterval
).build()
์ ๊ธฐ ์์ ์ ๊ณ ์ ํ ์ด๋ฆ์ผ๋ก ์๋ณ๋์ด์ผ ํ๋ฉฐ ๊ธฐ์กด ์์ฒญ์ ๋์ผํ ์ด๋ฆ์ด ์๋ ๊ฒฝ์ฐ ์ด ์์ฒญ์ ๋ฌด์ํ ์ง ์ด์ ์์ฒญ์ ์ทจ์ํ๊ณ ๋์ฒดํ ์ง ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ExistingPeriodicWorkPolicy๊ฐ ํฌํจ๋์ด์ผ ํฉ๋๋ค.
ExistingPeriodicWorkPolicy: ์ถฉ๋ ์ ๊ณ ์ ํ PeriodicWorkRequest์ ์ฌ์ฉํ ์ ์๋ ์ถฉ๋ ํด๊ฒฐ ์ ์ฑ ์ ์ด๊ฑฐํฉ๋๋ค.
ExistingPeriodicWorkPolicy.KEEP: ๋์ผํ ๊ณ ์ ์ด๋ฆ์ ๊ฐ์ง ๊ธฐ์กด ๋ณด๋ฅ(์๋ฃ๋์ง ์์) ์์ ์ด ์๋ ๊ฒฝ์ฐ ์๋ฌด ์์ ๋ ์ํํ์ง ์์ต๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์๋ก ์ง์ ๋ ์์ ์ ์ฝ์ ํ์ญ์์ค.
ExistingPeriodicWorkPolicy.REPLACE: ๋์ผํ ๊ณ ์ ์ด๋ฆ์ ๊ฐ์ง ๊ธฐ์กด ๋ณด๋ฅ(๋ฏธ์๋ฃ) ์์ ์ด ์๋ ๊ฒฝ์ฐ ์ทจ์ ๋ฐ ์ญ์ ํ ์๋ก ์ง์ ๋ ์์ ์ ์ฝ์ ํฉ๋๋ค.
WorkManager.getInstance().enqueueUniquePeriodicWork(
"Unique Name",
ExistingPeriodicWorkPolicy.KEEP, // or REPLACE
repeatingRequest
)
์์ ์ ์ํํ๊ธฐ ์ํด ์ ๋ ฅ ๋ฐ์ดํฐ๊ฐ ํ์ํ ์ ์์ต๋๋ค. ์ ๋ ฅ ๊ฐ์ ๋ฐ์ดํฐ ๊ฐ์ฒด์ key-value ์์ผ๋ก ์ ์ฅ๋๋ฉฐ ์์ ์์ฒญ ์ ์ค์ ํ ์ ์์ต๋๋ค.
์
๋ ฅ ๊ฐ์ ๊ฒฝ์ฐ inputData ๊ฐ์ฒด์ ์ ๊ทผํ์ฌ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ Result.success(output)
์ ์ฌ์ฉํ์ฌ ์์
์ ์ถ๋ ฅ์ผ๋ก ์ค์ ๋ฉ๋๋ค.
class MathWorker(context: Context, params: WorkerParameters):
CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val x = inputData.getInt(KEY_X_ARG, 0)
val y = inputData.getInt(KEY_Y_ARG, 0)
val result = computeMathFunction(x, y)
val output: Data = workDataOf(KEY_RESULT to result)
return Result.success(output)
}
}
doWork()
์์ ๋ฐํ๋๋ Result
๊ฐ์ฒด์ ๊ฒฝ์ฐ ์ฑ๊ณต๊ณผ ์คํจ ์ ํ์ ์ผ๋ก ์ถ๋ ฅ Data
๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์์ต๋๋ค.
WorkRequest
๋ฅผ ํตํด inputData๋ฅผ Worker
์ ์ ๋ฌํฉ๋๋ค.
val complexMathWork = OneTimeWorkRequest<MathWorker>()
.setInputData(
workDataOf(
"X_ARG" to 42,
"Y_ARG" to 421,
)
).build()
WorkManager.getInstance(myContext).enqueue(complexMathWork)
Worker
๊ฐ ์คํ๋์ด์ผ ํ ๋ ์ ์ฝ ์กฐ๊ฑด์ ์ง์ ํ ์ ์์ต๋๋ค. Constraints.Builder
๋ฅผ ์ฌ์ฉํ์ฌ WorkRequest
๋ด์์ ์ ์ํ ์ ์์ต๋๋ค.
setRequiredNetworkType() : ์ฅ์น๊ฐ WorkRequest๋ฅผ ์คํํ ํน์ NetworkType์ ๊ฐ์ ธ์ผ ํ๋์ง๋ฅผ ์ค์ ํฉ๋๋ค. NetworkType.UNMETERED๋ ์ฅ์น์ ๋คํธ์ํฌ ์ ํ์ ๋ฐ์ดํฐ ์ํ ๋๋ ์ ํ์ด ์์์ ์๋ฏธํฉ๋๋ค.(์ผ๋ฐ์ ์ผ๋ก Wi-Fi๋ฅผ ์๋ฏธํจ)
setRequiresCharging() : ์ถฉ์ ์ ์ํด ๊ธฐ๊ธฐ๋ฅผ ์ฐ๊ฒฐํด์ผ ํ๋์ง.
setRequiresBatteryNotLow() : ์ฅ์น ๋ฐฐํฐ๋ฆฌ๊ฐ ๋ฎ์์๋ ์ ๋ฉ๋๋ค.
setRequiresDeviceIdle() : WorkRequest๋ฅผ ์คํํ๋ ค๋ฉด ์ฅ์น๊ฐ ์ ํด ์ํ์ฌ์ผ ํฉ๋๋ค.
์ ๊ธฐ์ ์ธ ์์
์ ์ ์ฝ์ ์ ์ฉํ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ์ ์๋ ๋ฐ๋ณต ๊ฐ๊ฒฉ์ด ์ง๋๋ ์ ์ฝ ์กฐ๊ฑด์ ์ถฉ์กฑ๋ ๋๊น์ง PeriodicWorkRequest
๊ฐ ์คํ๋์ง ์์ต๋๋ค.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.setRequiresBatteryNotLow(true)
.setRequiresDeviceIdle(true)
.build()
val myWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()