본문 바로가기

안드로이드 Android/Android Study Jams

9. Repository and workManager - WorkManager

Setup and starter code walkthrough

Step 1: 스타터 앱 다운로드받고 실행하기

github.com/google-developer-training/android-kotlin-fundamentals-apps/tree/master/DevBytesRepository

 

google-developer-training/android-kotlin-fundamentals-apps

android-kotlin-fundamentals-apps. Contribute to google-developer-training/android-kotlin-fundamentals-apps development by creating an account on GitHub.

github.com

 

Step 2: 코드 살펴보기

 

 

 

Concept: WorkManager

developer.android.com/codelabs/kotlin-android-training-work-manager?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fkotlin-fundamentals-nine%23codelab-%2Fcodelabs%2Fkotlin-android-training-work-manager#3

 

Android Kotlin Fundamentals: WorkManager  |  Android 개발자

In this codelab, you learn how to use WorkManager to schedule background tasks in an efficient and optimized way in your Android Kotlin app.

developer.android.com

 

 

 

Add the WorkManager dependency

build.gradle (Module:app)

dependencies {
    ...

    // WorkManager dependency
    def work_version = "1.0.1"
    implementation "android.arch.work:work-runtime-ktx:$work_version"
}

 

 

 

Create a background worker

Step 1: worker 생성하기

work/RefreshDataWorker.kt

class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
        CoroutineWorker(appContext, params) {
    override suspend fun doWork(): Result {
        return Result.success()
    }
}

suspending function은 나중에 중단되고 재개될 수 있는 함수이다. suspending function은 오랫동안 실행되는 작업을 수행할 수 있고 메인 스레드가 블락되지 않고 완료되기를 기다릴 수 있다.

 

Step 2: doWork() 구현하기

RefreshDataWorker.kt

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( )
            Timber.d("Work request for sync is run")
        } catch (e: HttpException) {
            return Result.retry()
        }
        return Result.success()
    }
}

 

 

 

Define a periodic WorkRequest

Note: periodic work의 최소 간격은 15분이다. periodic work의 제약 중 하나로 periodic work은 초기 delay를 가질 수 없다.

 

Step 1: 반복적인 작업 설정하기

DevByteApplication.kt

    /**
     * Setup WorkManager background job to 'fetch' new network data daily.
     */
    private fun setupRecurringWork() {
        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
                .build()
    }

 

Step 2: WorkManager로 WorkRequest 스케줄링하기

RefreshDataWorker.kt

    companion object {
        const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
    }

 

DevByteApplication.kt

class DevByteApplication : Application() {
    private val applicationScope = CoroutineScope(Dispatchers.Default)

    /**
     * onCreate is called before the first screen is shown to the user.
     *
     * Use it to setup any background tasks, running expensive setup operations in a background
     * thread to avoid delaying app start.
     */
    override fun onCreate() {
        super.onCreate()
        delayedInit()
    }

    /**
     * Setup WorkManager background job to 'fetch' new network data daily.
     */
    private fun setupRecurringWork() {
        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
                .build()

        WorkManager.getInstance().enqueueUniquePeriodicWork(
                RefreshDataWorker.WORK_NAME,
                ExistingPeriodicWorkPolicy.KEEP,
                repeatingRequest)
    }

    private fun delayedInit() {
        applicationScope.launch {
            Timber.plant(Timber.DebugTree())
            setupRecurringWork()
        }
    }
}

같은 이름의 보류된(끝나지 않은) 작업이 존재하면 ExistingPeriodicWorkPolicy.KEEP 매개변수가 WorkManager가 이전의 periodic work를 유지하고 요청된 새로운 작업을 폐기하도록 한다.

Best Practice: onCreate() 메서드는 메인 스레드에서 실행된다. onCreate()에서 long-running operation을 수행한느 것은 UI 스레드를 블락할 수 있고 앱 로딩 딜레이를 발생시킬 수 있다. 이 문제를 피하기 위해 메인 스레드에서 벗어나 코루틴 안에서 Timber 초기화와 WorkManager 스케줄링 같은 task를 수행한다.

 

Step 3: (Optional) 최소 간격으로 WorkRequest 스케줄링하기

//        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//                .build()
        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
                .build()

 

 

 

Add constraints

developer.android.com/codelabs/kotlin-android-training-work-manager?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fkotlin-fundamentals-nine%23codelab-%2Fcodelabs%2Fkotlin-android-training-work-manager#7

 

Android Kotlin Fundamentals: WorkManager  |  Android 개발자

In this codelab, you learn how to use WorkManager to schedule background tasks in an efficient and optimized way in your Android Kotlin app.

developer.android.com

 

Step 1: Constraint 객체를 추가하고 제약 하나를 설정하기

DevByteApplication.kt

    private fun setupRecurringWork() {
        val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED)
                .build()

//        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//                .build()
        val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
                .setConstraints(constraints)
                .build()

        ...
    }

 

Step 2: 앱을 실행하고 로그 확인하기

 

Step 3: 더 많은 제약 추가하기

DevByteApplication.kt

    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)
    }

 

 

 

Homework

Q1. 2 / Q2. 1 / Q3. 1

 

 

 

Quiz

1. 3 / 2. 1 / 3. 2 / 4. 1 / 5. 2, 3 / 6. 1 / 7. 1